Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/stage-tamagotchi/src/main/windows/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ export async function setupMainWindow(params: {
webPreferences: {
preload: join(dirname(fileURLToPath(import.meta.url)), '../preload/index.mjs'),
sandbox: false,
// Disable webSecurity in development to avoid CORS issues
webSecurity: !is.dev,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Disabling webSecurity is a significant security risk, even in development mode. It makes your application vulnerable to cross-site scripting (XSS) attacks if it loads any third-party content in the renderer. While convenient for bypassing CORS issues, a more secure and production-like approach is to proxy requests through the main process. The main process is not subject to CORS, so it can safely fetch data from external APIs and forward it to the renderer process via IPC. This keeps webSecurity enabled, hardening your app against potential vulnerabilities.

},
// Thanks to [@HeartArmy](https://github.com/HeartArmy) for the tip implementation.
//
Expand Down
25 changes: 23 additions & 2 deletions packages/stage-ui/src/stores/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2072,6 +2072,10 @@ export const useProvidersStore = defineStore('providers', () => {
if (!providerMetadata[providerId] || intervalMs <= 0)
continue

// Only start periodic validation for providers that have been explicitly added
if (!addedProviders.value[providerId])
continue

if (providerRevalidationLoops.has(providerId)) {
continue
}
Expand All @@ -2087,8 +2091,8 @@ export const useProvidersStore = defineStore('providers', () => {
// Update configuration status for all configured providers
async function updateConfigurationStatus() {
await Promise.all(Object.entries(providerMetadata)
// TODO: ignore un-configured provider
// .filter(([_, provider]) => provider.configured)
// Only validate providers that have been explicitly added by the user
.filter(([providerId]) => addedProviders.value[providerId])
.map(async ([providerId]) => {
try {
if (providerRuntimeState.value[providerId]) {
Expand All @@ -2108,6 +2112,23 @@ export const useProvidersStore = defineStore('providers', () => {
watch(providerCredentials, updateConfigurationStatus, { deep: true, immediate: true })
startPeriodicRuntimeValidation()

// Watch for newly added providers and start their periodic validation
watch(addedProviders, (newAdded, oldAdded) => {
for (const providerId of Object.keys(newAdded)) {
if (newAdded[providerId] && !oldAdded?.[providerId]) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Detect added providers without comparing deep-watch old value

markProviderAdded() mutates addedProviders.value in place, and this watcher is deep: true, so on nested mutations Vue passes the same object for newAdded and oldAdded. That makes !oldAdded?.[providerId] false even when a key is newly inserted, so providers added after startup never enter this block and never get their periodic validation loop (contradicting the intended “watch newly added providers” behavior).

Useful? React with 👍 / 👎.

// Provider was just added, start its periodic validation if configured
const intervalMs = providerValidationIntervalMsById.get(providerId)
if (intervalMs && intervalMs > 0 && !providerRevalidationLoops.has(providerId)) {
const loop = useIntervalFn(() => {
void validateProvider(providerId, { force: true })
}, intervalMs, { immediate: false, immediateCallback: false })
loop.resume()
providerRevalidationLoops.set(providerId, loop)
}
}
}
}, { deep: true })
Comment on lines +2116 to +2130
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This watch block only handles provider additions. When a provider is removed, its periodic validation interval continues to run, leading to a resource leak. You should also handle provider removal by stopping the corresponding interval. Note that broader refactoring for type safety should be addressed in a separate pull request to maintain a focused scope.

  watch(addedProviders, (newAdded, oldAdded) => {
    const allIds = new Set([...Object.keys(newAdded), ...Object.keys(oldAdded || {})]);
    for (const providerId of allIds) {
      const isNew = newAdded[providerId];
      const wasOld = oldAdded?.[providerId];

      if (isNew && !wasOld) {
        // Provider was just added, start its periodic validation if configured
        const intervalMs = providerValidationIntervalMsById.get(providerId);
        if (intervalMs && intervalMs > 0 && !providerRevalidationLoops.has(providerId)) {
          const loop = useIntervalFn(() => {
            void validateProvider(providerId, { force: true });
          }, intervalMs, { immediate: false, immediateCallback: false });
          loop.resume();
          providerRevalidationLoops.set(providerId, loop);
        }
      } else if (!isNew && wasOld) {
        // Provider was removed, stop its periodic validation
        const loop = providerRevalidationLoops.get(providerId);
        if (loop) {
          (loop as { pause: () => void }).pause();
          providerRevalidationLoops.delete(providerId);
        }
      }
    }
  }, { deep: true })
References
  1. Pull requests should have a focused scope. Broader refactoring, such as modifying shared interfaces for better type safety, should be addressed in a separate pull request to avoid scope creep.


watch(() => authState.isAuthenticated, updateConfigurationStatus)

// Available providers (only those that are properly configured)
Expand Down