@agent-native/core 0.95.1 → 0.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +64 -0
- package/corpus/core/docs/content/audit-log.mdx +2 -1
- package/corpus/core/docs/content/automations.mdx +7 -7
- package/corpus/core/docs/content/creating-templates.mdx +1 -1
- package/corpus/core/docs/content/internationalization.mdx +2 -1
- package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/ar-SA/security.mdx +8 -0
- package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
- package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/de-DE/security.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
- package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/es-ES/security.mdx +8 -0
- package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
- package/corpus/core/docs/content/locales/fr-FR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
- package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/hi-IN/security.mdx +8 -0
- package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/ja-JP/security.mdx +8 -0
- package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
- package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/ko-KR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/pt-BR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
- package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/zh-CN/security.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
- package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/zh-TW/security.mdx +8 -0
- package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
- package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
- package/corpus/core/package.json +11 -2
- package/corpus/core/src/a2a/auth-policy.ts +33 -0
- package/corpus/core/src/a2a/client.ts +11 -1
- package/corpus/core/src/agent/durable-background.ts +15 -7
- package/corpus/core/src/agent/production-agent.ts +87 -27
- package/corpus/core/src/agent/run-store.ts +13 -4
- package/corpus/core/src/agent/thread-data-builder.ts +7 -5
- package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
- package/corpus/core/src/audit/store.ts +8 -3
- package/corpus/core/src/audit/types.ts +3 -0
- package/corpus/core/src/chat-threads/store.ts +20 -1
- package/corpus/core/src/cli/code-agent-executor.ts +1 -0
- package/corpus/core/src/cli/code-agent-runs.ts +14 -0
- package/corpus/core/src/cli/doctor.ts +396 -0
- package/corpus/core/src/cli/index.ts +56 -0
- package/corpus/core/src/cli/info.ts +3 -3
- package/corpus/core/src/cli/package-lifecycle.ts +821 -0
- package/corpus/core/src/client/AgentPanel.tsx +32 -25
- package/corpus/core/src/client/AssistantChat.tsx +36 -20
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
- package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
- package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
- package/corpus/core/src/client/chat/index.ts +6 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +37 -3
- package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
- package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
- package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
- package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
- package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
- package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
- package/corpus/core/src/client/guided-questions.tsx +2 -1
- package/corpus/core/src/client/index.ts +11 -0
- package/corpus/core/src/client/use-chat-threads.ts +10 -1
- package/corpus/core/src/client/voice-provider-status.ts +44 -0
- package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
- package/corpus/core/src/deploy/build.ts +12 -11
- package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
- package/corpus/core/src/guards/index.ts +23 -0
- package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
- package/corpus/core/src/guards/no-env-credentials.ts +229 -0
- package/corpus/core/src/guards/no-env-mutation.ts +114 -0
- package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
- package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
- package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
- package/corpus/core/src/guards/scan-utils.ts +105 -0
- package/corpus/core/src/guards/types.ts +30 -0
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
- package/corpus/core/src/integrations/adapters/slack.ts +74 -21
- package/corpus/core/src/integrations/webhook-handler.ts +14 -0
- package/corpus/core/src/localization/default-messages.ts +6 -0
- package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
- package/corpus/core/src/observability/routes.ts +17 -0
- package/corpus/core/src/observability/store.ts +4 -1
- package/corpus/core/src/observability/traces.ts +41 -2
- package/corpus/core/src/org/accept-pending.ts +9 -1
- package/corpus/core/src/org/migrations.ts +41 -0
- package/corpus/core/src/package-lifecycle/index.ts +1 -0
- package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
- package/corpus/core/src/provider-api/custom-registry.ts +56 -0
- package/corpus/core/src/provider-api/index.ts +2 -0
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
- package/corpus/core/src/scripts/call-agent.ts +53 -3
- package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
- package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
- package/corpus/core/src/secrets/storage.ts +47 -27
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +95 -18
- package/corpus/core/src/server/core-routes-plugin.ts +45 -14
- package/corpus/core/src/server/csrf.ts +24 -5
- package/corpus/core/src/server/framework-request-handler.ts +34 -0
- package/corpus/core/src/server/google-realtime-session.ts +1 -36
- package/corpus/core/src/server/poll.ts +24 -2
- package/corpus/core/src/server/realtime-voice.ts +80 -24
- package/corpus/core/src/server/request-origin.ts +63 -0
- package/corpus/core/src/server/transcribe-voice.ts +1 -50
- package/corpus/core/src/server/voice-providers-status.ts +4 -3
- package/corpus/core/src/shared/cache-control.ts +2 -8
- package/corpus/core/src/styles/agent-native.css +1 -0
- package/corpus/core/src/styles/chat-history-list.css +313 -0
- package/corpus/core/src/templates/default/package.json +2 -2
- package/corpus/core/src/templates/headless/package.json +0 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/corpus/core/src/templates/workspace-core/package.json +0 -1
- package/corpus/core/src/templates/workspace-root/package.json +0 -1
- package/corpus/core/src/triggers/actions.ts +11 -3
- package/corpus/core/src/triggers/types.ts +7 -1
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/AGENTS.md +7 -0
- package/corpus/templates/analytics/actions/bigquery.ts +26 -2
- package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
- package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
- package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
- package/corpus/templates/analytics/app/global.css +10 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
- package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
- package/corpus/templates/analytics/package.json +1 -2
- package/corpus/templates/analytics/server/lib/bigquery.ts +91 -14
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
- package/corpus/templates/assets/package.json +1 -2
- package/corpus/templates/brain/package.json +1 -2
- package/corpus/templates/calendar/package.json +1 -2
- package/corpus/templates/chat/package.json +1 -2
- package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
- package/corpus/templates/clips/chrome-extension/package.json +3 -2
- package/corpus/templates/clips/desktop/package.json +6 -4
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
- package/corpus/templates/clips/package.json +1 -2
- package/corpus/templates/clips/server/db/schema.ts +29 -17
- package/corpus/templates/clips/server/plugins/db.ts +8 -0
- package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
- package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
- package/corpus/templates/content/AGENTS.md +72 -62
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
- package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
- package/corpus/templates/content/actions/_position-utils.ts +76 -0
- package/corpus/templates/content/actions/_property-utils.ts +31 -20
- package/corpus/templates/content/actions/add-database-item.ts +52 -39
- package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
- package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
- package/corpus/templates/content/actions/configure-document-property.ts +36 -27
- package/corpus/templates/content/actions/create-content-database.ts +40 -30
- package/corpus/templates/content/actions/create-document.ts +39 -32
- package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
- package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
- package/corpus/templates/content/actions/move-document.ts +64 -48
- package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
- package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
- package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
- package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
- package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/content/app/i18n-data.ts +40 -0
- package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
- package/corpus/templates/content/package.json +1 -2
- package/corpus/templates/content/parity/matrix.md +25 -25
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
- package/corpus/templates/content/shared/api.ts +16 -1
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
- package/corpus/templates/design/AGENTS.md +12 -2
- package/corpus/templates/design/DEVELOPING.md +10 -6
- package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
- package/corpus/templates/design/actions/duplicate-design.ts +31 -25
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
- package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
- package/corpus/templates/design/package.json +1 -1
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
- package/corpus/templates/dispatch/package.json +1 -2
- package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
- package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
- package/corpus/templates/forms/package.json +1 -2
- package/corpus/templates/macros/package.json +1 -2
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
- package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
- package/corpus/templates/mail/package.json +1 -2
- package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
- package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
- package/corpus/templates/plan/package.json +1 -2
- package/corpus/templates/plan/server/plans.ts +9 -2
- package/corpus/templates/slides/actions/list-decks.ts +28 -0
- package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
- package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
- package/corpus/templates/slides/app/pages/Index.tsx +2 -3
- package/corpus/templates/slides/app/root.tsx +0 -5
- package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
- package/corpus/templates/slides/package.json +1 -2
- package/dist/a2a/auth-policy.d.ts +19 -0
- package/dist/a2a/auth-policy.d.ts.map +1 -1
- package/dist/a2a/auth-policy.js +32 -0
- package/dist/a2a/auth-policy.js.map +1 -1
- package/dist/a2a/client.d.ts +7 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +2 -1
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/durable-background.d.ts +8 -5
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +15 -8
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +63 -26
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +9 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +6 -5
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/audit/actions/export-audit-events.d.ts +27 -0
- package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
- package/dist/audit/actions/export-audit-events.js +144 -0
- package/dist/audit/actions/export-audit-events.js.map +1 -0
- package/dist/audit/store.d.ts +1 -0
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +8 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/audit/types.d.ts +3 -0
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js.map +1 -1
- package/dist/chat-threads/store.d.ts +10 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +6 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +1 -0
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.d.ts +12 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +1 -0
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/doctor.d.ts +83 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +277 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/info.js +3 -3
- package/dist/cli/info.js.map +1 -1
- package/dist/cli/package-lifecycle.d.ts +50 -0
- package/dist/cli/package-lifecycle.d.ts.map +1 -0
- package/dist/cli/package-lifecycle.js +560 -0
- package/dist/cli/package-lifecycle.js.map +1 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +3 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +11 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -4
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +83 -10
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.js +7 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/ChatHistoryList.d.ts +88 -0
- package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
- package/dist/client/chat/ChatHistoryList.js +83 -0
- package/dist/client/chat/ChatHistoryList.js.map +1 -0
- package/dist/client/chat/index.d.ts +1 -0
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -0
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +13 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +10 -2
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/code-agent-chat-adapter.d.ts +17 -1
- package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +47 -3
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.js +5 -3
- package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +25 -2
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +96 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +21 -1
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
- package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
- package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
- package/dist/client/conversation/code-agent-transcript.js +3 -5
- package/dist/client/conversation/code-agent-transcript.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +2 -1
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +9 -1
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/voice-provider-status.d.ts +10 -0
- package/dist/client/voice-provider-status.d.ts.map +1 -0
- package/dist/client/voice-provider-status.js +32 -0
- package/dist/client/voice-provider-status.js.map +1 -0
- package/dist/code-agents/transcript-normalizer.d.ts +29 -0
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +55 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +13 -11
- package/dist/deploy/build.js.map +1 -1
- package/dist/guards/db-tool-scoping.d.ts +38 -0
- package/dist/guards/db-tool-scoping.d.ts.map +1 -0
- package/dist/guards/db-tool-scoping.js +114 -0
- package/dist/guards/db-tool-scoping.js.map +1 -0
- package/dist/guards/index.d.ts +23 -0
- package/dist/guards/index.d.ts.map +1 -0
- package/dist/guards/index.js +19 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/guards/no-drizzle-push.d.ts +14 -0
- package/dist/guards/no-drizzle-push.d.ts.map +1 -0
- package/dist/guards/no-drizzle-push.js +97 -0
- package/dist/guards/no-drizzle-push.js.map +1 -0
- package/dist/guards/no-env-credentials.d.ts +36 -0
- package/dist/guards/no-env-credentials.d.ts.map +1 -0
- package/dist/guards/no-env-credentials.js +204 -0
- package/dist/guards/no-env-credentials.js.map +1 -0
- package/dist/guards/no-env-mutation.d.ts +23 -0
- package/dist/guards/no-env-mutation.d.ts.map +1 -0
- package/dist/guards/no-env-mutation.js +90 -0
- package/dist/guards/no-env-mutation.js.map +1 -0
- package/dist/guards/no-localhost-fallback.d.ts +30 -0
- package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
- package/dist/guards/no-localhost-fallback.js +105 -0
- package/dist/guards/no-localhost-fallback.js.map +1 -0
- package/dist/guards/no-unscoped-credentials.d.ts +28 -0
- package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
- package/dist/guards/no-unscoped-credentials.js +199 -0
- package/dist/guards/no-unscoped-credentials.js.map +1 -0
- package/dist/guards/no-unscoped-queries.d.ts +32 -0
- package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
- package/dist/guards/no-unscoped-queries.js +720 -0
- package/dist/guards/no-unscoped-queries.js.map +1 -0
- package/dist/guards/scan-utils.d.ts +28 -0
- package/dist/guards/scan-utils.d.ts.map +1 -0
- package/dist/guards/scan-utils.js +92 -0
- package/dist/guards/scan-utils.js.map +1 -0
- package/dist/guards/types.d.ts +28 -0
- package/dist/guards/types.d.ts.map +1 -0
- package/dist/guards/types.js +11 -0
- package/dist/guards/types.js.map +1 -0
- package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +25 -5
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +55 -21
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +11 -0
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +5 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/hosted-model-experiment.d.ts +39 -0
- package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
- package/dist/observability/hosted-model-experiment.js +90 -0
- package/dist/observability/hosted-model-experiment.js.map +1 -0
- package/dist/observability/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts.map +1 -1
- package/dist/observability/routes.js +16 -0
- package/dist/observability/routes.js.map +1 -1
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +4 -1
- package/dist/observability/store.js.map +1 -1
- package/dist/observability/traces.d.ts +6 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +28 -2
- package/dist/observability/traces.js.map +1 -1
- package/dist/org/accept-pending.d.ts.map +1 -1
- package/dist/org/accept-pending.js +9 -1
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/migrations.d.ts +7 -2
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +41 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/package-lifecycle/index.d.ts +2 -0
- package/dist/package-lifecycle/index.d.ts.map +1 -0
- package/dist/package-lifecycle/index.js +2 -0
- package/dist/package-lifecycle/index.js.map +1 -0
- package/dist/package-lifecycle/manifest.d.ts +24 -0
- package/dist/package-lifecycle/manifest.d.ts.map +1 -0
- package/dist/package-lifecycle/manifest.js +81 -0
- package/dist/package-lifecycle/manifest.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/custom-registry.d.ts +32 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +41 -1
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/index.d.ts +1 -1
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +1 -1
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +43 -3
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/chat/search-chats.d.ts +1 -1
- package/dist/scripts/chat/search-chats.d.ts.map +1 -1
- package/dist/scripts/chat/search-chats.js +11 -8
- package/dist/scripts/chat/search-chats.js.map +1 -1
- package/dist/secrets/register-framework-secrets.js +1 -1
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +45 -27
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +11 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +74 -19
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +3 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +36 -12
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +18 -5
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +34 -0
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +2 -34
- package/dist/server/google-realtime-session.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +24 -2
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +64 -21
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/server/request-origin.d.ts +10 -0
- package/dist/server/request-origin.d.ts.map +1 -0
- package/dist/server/request-origin.js +60 -0
- package/dist/server/request-origin.js.map +1 -0
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +2 -48
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +2 -3
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/shared/cache-control.d.ts +4 -6
- package/dist/shared/cache-control.d.ts.map +1 -1
- package/dist/shared/cache-control.js +2 -7
- package/dist/shared/cache-control.js.map +1 -1
- package/dist/styles/agent-native.css +1 -0
- package/dist/styles/chat-history-list.css +313 -0
- package/dist/templates/default/package.json +2 -2
- package/dist/templates/headless/package.json +0 -1
- package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/dist/templates/workspace-core/package.json +0 -1
- package/dist/templates/workspace-root/package.json +0 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +8 -3
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/types.d.ts +7 -1
- package/dist/triggers/types.d.ts.map +1 -1
- package/dist/triggers/types.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/audit-log.mdx +2 -1
- package/docs/content/automations.mdx +7 -7
- package/docs/content/creating-templates.mdx +1 -1
- package/docs/content/internationalization.mdx +2 -1
- package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
- package/docs/content/locales/ar-SA/automations.mdx +7 -7
- package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
- package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
- package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
- package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/ar-SA/security.mdx +8 -0
- package/docs/content/locales/de-DE/audit-log.mdx +2 -1
- package/docs/content/locales/de-DE/automations.mdx +7 -7
- package/docs/content/locales/de-DE/getting-started.mdx +7 -1
- package/docs/content/locales/de-DE/internationalization.mdx +1 -1
- package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
- package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/de-DE/security.mdx +8 -0
- package/docs/content/locales/es-ES/audit-log.mdx +2 -1
- package/docs/content/locales/es-ES/automations.mdx +7 -7
- package/docs/content/locales/es-ES/getting-started.mdx +7 -1
- package/docs/content/locales/es-ES/internationalization.mdx +1 -1
- package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
- package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/es-ES/security.mdx +8 -0
- package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
- package/docs/content/locales/fr-FR/automations.mdx +7 -7
- package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
- package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
- package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
- package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
- package/docs/content/locales/fr-FR/security.mdx +8 -0
- package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
- package/docs/content/locales/hi-IN/automations.mdx +7 -7
- package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
- package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
- package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
- package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/hi-IN/security.mdx +8 -0
- package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
- package/docs/content/locales/ja-JP/automations.mdx +7 -7
- package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
- package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
- package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
- package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/ja-JP/security.mdx +8 -0
- package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
- package/docs/content/locales/ko-KR/automations.mdx +7 -7
- package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
- package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
- package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
- package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/ko-KR/security.mdx +8 -0
- package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
- package/docs/content/locales/pt-BR/automations.mdx +7 -7
- package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
- package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
- package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
- package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/pt-BR/security.mdx +8 -0
- package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
- package/docs/content/locales/zh-CN/automations.mdx +7 -7
- package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
- package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
- package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
- package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/zh-CN/security.mdx +8 -0
- package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
- package/docs/content/locales/zh-TW/automations.mdx +7 -7
- package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
- package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
- package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
- package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/zh-TW/security.mdx +8 -0
- package/docs/content/package-lifecycle.mdx +51 -0
- package/docs/content/sandbox-adapters.mdx +16 -5
- package/package.json +12 -3
- package/src/templates/default/package.json +2 -2
- package/src/templates/headless/package.json +0 -1
- package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/src/templates/workspace-core/package.json +0 -1
- package/src/templates/workspace-root/package.json +0 -1
- package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
- package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
- package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanEnvMutation — ported from `scripts/guard-no-env-mutation.mjs`.
|
|
3
|
+
*
|
|
4
|
+
* Refuse to let production code MUTATE `process.env` (e.g.
|
|
5
|
+
* `process.env.AGENT_USER_EMAIL = userEmail`). `process.env` is
|
|
6
|
+
* process-scoped, not request-scoped — on serverless, every warm container
|
|
7
|
+
* handles many concurrent requests in one Node process, so mutating
|
|
8
|
+
* `process.env` in a request handler leaks state across requests. Use
|
|
9
|
+
* `runWithRequestContext` instead.
|
|
10
|
+
*
|
|
11
|
+
* Conditional guard — per report 005's V1 guard set table: the original
|
|
12
|
+
* has 9 `ALLOWED_PATH_PREDICATES`; 4 are monorepo-only
|
|
13
|
+
* (`packages/core/src/dev`, `templates/*\/test(s)/`, `packages/cli/`,
|
|
14
|
+
* `packages/create-agent-native/`) and dropped here. The 5 portable ones
|
|
15
|
+
* (`scripts/**`, `*.spec.*`, `*.test.*`, `/cli/`, `/scaffold/`) are kept
|
|
16
|
+
* verbatim — they already work unmodified for a generated app.
|
|
17
|
+
*
|
|
18
|
+
* Opt-out (same line, or the line immediately above):
|
|
19
|
+
* process.env.X = y // guard:allow-env-mutation — short reason
|
|
20
|
+
*/
|
|
21
|
+
import type { GuardResult, GuardScanOptions } from "./types.js";
|
|
22
|
+
export declare function scanEnvMutation(options: GuardScanOptions): GuardResult;
|
|
23
|
+
//# sourceMappingURL=no-env-mutation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-env-mutation.d.ts","sourceRoot":"","sources":["../../src/guards/no-env-mutation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AASH,OAAO,KAAK,EAAgB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+C9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,WAAW,CAsCtE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanEnvMutation — ported from `scripts/guard-no-env-mutation.mjs`.
|
|
3
|
+
*
|
|
4
|
+
* Refuse to let production code MUTATE `process.env` (e.g.
|
|
5
|
+
* `process.env.AGENT_USER_EMAIL = userEmail`). `process.env` is
|
|
6
|
+
* process-scoped, not request-scoped — on serverless, every warm container
|
|
7
|
+
* handles many concurrent requests in one Node process, so mutating
|
|
8
|
+
* `process.env` in a request handler leaks state across requests. Use
|
|
9
|
+
* `runWithRequestContext` instead.
|
|
10
|
+
*
|
|
11
|
+
* Conditional guard — per report 005's V1 guard set table: the original
|
|
12
|
+
* has 9 `ALLOWED_PATH_PREDICATES`; 4 are monorepo-only
|
|
13
|
+
* (`packages/core/src/dev`, `templates/*\/test(s)/`, `packages/cli/`,
|
|
14
|
+
* `packages/create-agent-native/`) and dropped here. The 5 portable ones
|
|
15
|
+
* (`scripts/**`, `*.spec.*`, `*.test.*`, `/cli/`, `/scaffold/`) are kept
|
|
16
|
+
* verbatim — they already work unmodified for a generated app.
|
|
17
|
+
*
|
|
18
|
+
* Opt-out (same line, or the line immediately above):
|
|
19
|
+
* process.env.X = y // guard:allow-env-mutation — short reason
|
|
20
|
+
*/
|
|
21
|
+
import { isCommentLine, lineColForOffset, readFileSafe, relPosix, walk, } from "./scan-utils.js";
|
|
22
|
+
const ALLOWED_PATH_PREDICATES = [
|
|
23
|
+
(rel) => /^scripts\//.test(rel),
|
|
24
|
+
(rel) => /\.spec\.[tj]sx?$/.test(rel),
|
|
25
|
+
(rel) => /\.test\.[tj]sx?$/.test(rel),
|
|
26
|
+
(rel) => /\/cli\//.test(rel),
|
|
27
|
+
(rel) => /\/scaffold\//.test(rel),
|
|
28
|
+
];
|
|
29
|
+
const OPT_OUT_MARKER = /\/\/\s*guard:allow-env-mutation\b[^\n]*/;
|
|
30
|
+
const OPT_OUT_REQUIRES_REASON = /\/\/\s*guard:allow-env-mutation\s*[—-]\s*\S/;
|
|
31
|
+
const ASSIGN_TAIL = String.raw `(?:\s*(?:=(?!=)|\+=|-=|\*=|/=|\?\?=|\|\|=|&&=))`;
|
|
32
|
+
const MEMBER_FORM = new RegExp(String.raw `process\.env\.[A-Z_][A-Z0-9_]*${ASSIGN_TAIL}`, "g");
|
|
33
|
+
const BRACKET_FORM = new RegExp(String.raw `process\.env\[\s*["'][A-Z_][A-Z0-9_]+["']\s*\]${ASSIGN_TAIL}`, "g");
|
|
34
|
+
const DELETE_MEMBER_FORM = new RegExp(String.raw `\bdelete\s+process\.env\.[A-Z_][A-Z0-9_]*\b`, "g");
|
|
35
|
+
const DELETE_BRACKET_FORM = new RegExp(String.raw `\bdelete\s+process\.env\[\s*["'][A-Z_][A-Z0-9_]+["']\s*\]`, "g");
|
|
36
|
+
function isAllowedPath(rel) {
|
|
37
|
+
return ALLOWED_PATH_PREDICATES.some((p) => p(rel));
|
|
38
|
+
}
|
|
39
|
+
function hasValidOptOut(lines, lineIdx) {
|
|
40
|
+
const cur = lines[lineIdx] ?? "";
|
|
41
|
+
if (OPT_OUT_MARKER.test(cur)) {
|
|
42
|
+
return OPT_OUT_REQUIRES_REASON.test(cur);
|
|
43
|
+
}
|
|
44
|
+
const prev = lines[lineIdx - 1] ?? "";
|
|
45
|
+
if (/^\s*\/\//.test(prev) && OPT_OUT_MARKER.test(prev)) {
|
|
46
|
+
return OPT_OUT_REQUIRES_REASON.test(prev);
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
export function scanEnvMutation(options) {
|
|
51
|
+
const { root } = options;
|
|
52
|
+
const findings = [];
|
|
53
|
+
for (const file of walk(root)) {
|
|
54
|
+
if (!/\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(file))
|
|
55
|
+
continue;
|
|
56
|
+
if (file.endsWith(".d.ts"))
|
|
57
|
+
continue;
|
|
58
|
+
const rel = relPosix(root, file);
|
|
59
|
+
if (isAllowedPath(rel))
|
|
60
|
+
continue;
|
|
61
|
+
const contents = readFileSafe(file);
|
|
62
|
+
if (contents === null || !contents.includes("process.env"))
|
|
63
|
+
continue;
|
|
64
|
+
const lines = contents.split("\n");
|
|
65
|
+
for (const re of [
|
|
66
|
+
MEMBER_FORM,
|
|
67
|
+
BRACKET_FORM,
|
|
68
|
+
DELETE_MEMBER_FORM,
|
|
69
|
+
DELETE_BRACKET_FORM,
|
|
70
|
+
]) {
|
|
71
|
+
re.lastIndex = 0;
|
|
72
|
+
let m;
|
|
73
|
+
while ((m = re.exec(contents)) !== null) {
|
|
74
|
+
const { line } = lineColForOffset(contents, m.index);
|
|
75
|
+
const lineText = lines[line - 1] ?? "";
|
|
76
|
+
if (isCommentLine(lineText))
|
|
77
|
+
continue;
|
|
78
|
+
if (hasValidOptOut(lines, line - 1))
|
|
79
|
+
continue;
|
|
80
|
+
findings.push({
|
|
81
|
+
file: rel,
|
|
82
|
+
line,
|
|
83
|
+
message: `process.env mutated in production code: ${lineText.trim()}. process.env is process-scoped, not request-scoped — use runWithRequestContext instead.`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { name: "no-env-mutation", findings };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=no-env-mutation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-env-mutation.js","sourceRoot":"","sources":["../../src/guards/no-env-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * scanEnvMutation — ported from `scripts/guard-no-env-mutation.mjs`.\n *\n * Refuse to let production code MUTATE `process.env` (e.g.\n * `process.env.AGENT_USER_EMAIL = userEmail`). `process.env` is\n * process-scoped, not request-scoped — on serverless, every warm container\n * handles many concurrent requests in one Node process, so mutating\n * `process.env` in a request handler leaks state across requests. Use\n * `runWithRequestContext` instead.\n *\n * Conditional guard — per report 005's V1 guard set table: the original\n * has 9 `ALLOWED_PATH_PREDICATES`; 4 are monorepo-only\n * (`packages/core/src/dev`, `templates/*\\/test(s)/`, `packages/cli/`,\n * `packages/create-agent-native/`) and dropped here. The 5 portable ones\n * (`scripts/**`, `*.spec.*`, `*.test.*`, `/cli/`, `/scaffold/`) are kept\n * verbatim — they already work unmodified for a generated app.\n *\n * Opt-out (same line, or the line immediately above):\n * process.env.X = y // guard:allow-env-mutation — short reason\n */\n\nimport {\n isCommentLine,\n lineColForOffset,\n readFileSafe,\n relPosix,\n walk,\n} from \"./scan-utils.js\";\nimport type { GuardFinding, GuardResult, GuardScanOptions } from \"./types.js\";\n\nconst ALLOWED_PATH_PREDICATES: Array<(rel: string) => boolean> = [\n (rel) => /^scripts\\//.test(rel),\n (rel) => /\\.spec\\.[tj]sx?$/.test(rel),\n (rel) => /\\.test\\.[tj]sx?$/.test(rel),\n (rel) => /\\/cli\\//.test(rel),\n (rel) => /\\/scaffold\\//.test(rel),\n];\n\nconst OPT_OUT_MARKER = /\\/\\/\\s*guard:allow-env-mutation\\b[^\\n]*/;\nconst OPT_OUT_REQUIRES_REASON = /\\/\\/\\s*guard:allow-env-mutation\\s*[—-]\\s*\\S/;\n\nconst ASSIGN_TAIL = String.raw`(?:\\s*(?:=(?!=)|\\+=|-=|\\*=|/=|\\?\\?=|\\|\\|=|&&=))`;\nconst MEMBER_FORM = new RegExp(\n String.raw`process\\.env\\.[A-Z_][A-Z0-9_]*${ASSIGN_TAIL}`,\n \"g\",\n);\nconst BRACKET_FORM = new RegExp(\n String.raw`process\\.env\\[\\s*[\"'][A-Z_][A-Z0-9_]+[\"']\\s*\\]${ASSIGN_TAIL}`,\n \"g\",\n);\nconst DELETE_MEMBER_FORM = new RegExp(\n String.raw`\\bdelete\\s+process\\.env\\.[A-Z_][A-Z0-9_]*\\b`,\n \"g\",\n);\nconst DELETE_BRACKET_FORM = new RegExp(\n String.raw`\\bdelete\\s+process\\.env\\[\\s*[\"'][A-Z_][A-Z0-9_]+[\"']\\s*\\]`,\n \"g\",\n);\n\nfunction isAllowedPath(rel: string): boolean {\n return ALLOWED_PATH_PREDICATES.some((p) => p(rel));\n}\n\nfunction hasValidOptOut(lines: string[], lineIdx: number): boolean {\n const cur = lines[lineIdx] ?? \"\";\n if (OPT_OUT_MARKER.test(cur)) {\n return OPT_OUT_REQUIRES_REASON.test(cur);\n }\n const prev = lines[lineIdx - 1] ?? \"\";\n if (/^\\s*\\/\\//.test(prev) && OPT_OUT_MARKER.test(prev)) {\n return OPT_OUT_REQUIRES_REASON.test(prev);\n }\n return false;\n}\n\nexport function scanEnvMutation(options: GuardScanOptions): GuardResult {\n const { root } = options;\n const findings: GuardFinding[] = [];\n\n for (const file of walk(root)) {\n if (!/\\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(file)) continue;\n if (file.endsWith(\".d.ts\")) continue;\n const rel = relPosix(root, file);\n if (isAllowedPath(rel)) continue;\n\n const contents = readFileSafe(file);\n if (contents === null || !contents.includes(\"process.env\")) continue;\n\n const lines = contents.split(\"\\n\");\n\n for (const re of [\n MEMBER_FORM,\n BRACKET_FORM,\n DELETE_MEMBER_FORM,\n DELETE_BRACKET_FORM,\n ]) {\n re.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = re.exec(contents)) !== null) {\n const { line } = lineColForOffset(contents, m.index);\n const lineText = lines[line - 1] ?? \"\";\n if (isCommentLine(lineText)) continue;\n if (hasValidOptOut(lines, line - 1)) continue;\n findings.push({\n file: rel,\n line,\n message: `process.env mutated in production code: ${lineText.trim()}. process.env is process-scoped, not request-scoped — use runWithRequestContext instead.`,\n });\n }\n }\n }\n\n return { name: \"no-env-mutation\", findings };\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanLocalhostFallback — ported from
|
|
3
|
+
* `scripts/guard-no-localhost-fallback.mjs`.
|
|
4
|
+
*
|
|
5
|
+
* Refuse to let production code use the literal `local@localhost` (or the
|
|
6
|
+
* `DEV_MODE_USER_EMAIL` symbolic alias) as a fallback identity when no
|
|
7
|
+
* session is present — that pools every unauthenticated request onto one
|
|
8
|
+
* shared tenant. The right behavior in production is to throw/401 when
|
|
9
|
+
* there's no session, not fall back to a sentinel identity.
|
|
10
|
+
*
|
|
11
|
+
* Conditional guard — per report 005's V1 guard set table: the original
|
|
12
|
+
* hardcodes framework-file exemptions (`packages/core/src/server/auth.ts`,
|
|
13
|
+
* `packages/core/src/org/context.ts`, etc. — files that either define or
|
|
14
|
+
* intentionally handle the dev-mode sentinel). Those live in `node_modules`
|
|
15
|
+
* for a generated app and are already excluded by `SKIP_DIRS`, so they're
|
|
16
|
+
* moved behind an optional `extraExemptPaths` param (default `[]`) instead
|
|
17
|
+
* of hardcoded. The generic allowlist predicates (`*.spec.*`, `*.test.*`,
|
|
18
|
+
* `scripts/**`, `**\/seed(s)/**`) are kept verbatim.
|
|
19
|
+
*
|
|
20
|
+
* Opt-out (same line, or the line immediately above):
|
|
21
|
+
* const x = email ?? "local@localhost" // guard:allow-localhost-fallback — short reason
|
|
22
|
+
*/
|
|
23
|
+
import type { GuardResult, GuardScanOptions } from "./types.js";
|
|
24
|
+
export interface LocalhostFallbackOptions extends GuardScanOptions {
|
|
25
|
+
/** Exact repo-relative paths to exempt in addition to the generic
|
|
26
|
+
* predicates (spec/test/scripts/seed). Default `[]`. */
|
|
27
|
+
extraExemptPaths?: string[];
|
|
28
|
+
}
|
|
29
|
+
export declare function scanLocalhostFallback(options: LocalhostFallbackOptions): GuardResult;
|
|
30
|
+
//# sourceMappingURL=no-localhost-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-localhost-fallback.d.ts","sourceRoot":"","sources":["../../src/guards/no-localhost-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH,OAAO,KAAK,EAAgB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE;4DACwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAsCD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,wBAAwB,GAChC,WAAW,CA+Cb"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanLocalhostFallback — ported from
|
|
3
|
+
* `scripts/guard-no-localhost-fallback.mjs`.
|
|
4
|
+
*
|
|
5
|
+
* Refuse to let production code use the literal `local@localhost` (or the
|
|
6
|
+
* `DEV_MODE_USER_EMAIL` symbolic alias) as a fallback identity when no
|
|
7
|
+
* session is present — that pools every unauthenticated request onto one
|
|
8
|
+
* shared tenant. The right behavior in production is to throw/401 when
|
|
9
|
+
* there's no session, not fall back to a sentinel identity.
|
|
10
|
+
*
|
|
11
|
+
* Conditional guard — per report 005's V1 guard set table: the original
|
|
12
|
+
* hardcodes framework-file exemptions (`packages/core/src/server/auth.ts`,
|
|
13
|
+
* `packages/core/src/org/context.ts`, etc. — files that either define or
|
|
14
|
+
* intentionally handle the dev-mode sentinel). Those live in `node_modules`
|
|
15
|
+
* for a generated app and are already excluded by `SKIP_DIRS`, so they're
|
|
16
|
+
* moved behind an optional `extraExemptPaths` param (default `[]`) instead
|
|
17
|
+
* of hardcoded. The generic allowlist predicates (`*.spec.*`, `*.test.*`,
|
|
18
|
+
* `scripts/**`, `**\/seed(s)/**`) are kept verbatim.
|
|
19
|
+
*
|
|
20
|
+
* Opt-out (same line, or the line immediately above):
|
|
21
|
+
* const x = email ?? "local@localhost" // guard:allow-localhost-fallback — short reason
|
|
22
|
+
*/
|
|
23
|
+
import { isCommentLine, lineColForOffset, readFileSafe, relPosix, walk, } from "./scan-utils.js";
|
|
24
|
+
const GENERIC_ALLOWED_PATH_PREDICATES = [
|
|
25
|
+
(rel) => /\.spec\.[tj]sx?$/.test(rel),
|
|
26
|
+
(rel) => /\.test\.[tj]sx?$/.test(rel),
|
|
27
|
+
(rel) => /^scripts\//.test(rel),
|
|
28
|
+
(rel) => /\/seed\//.test(rel),
|
|
29
|
+
(rel) => /\/seeds\//.test(rel),
|
|
30
|
+
];
|
|
31
|
+
const OPT_OUT_MARKER = /\/\/\s*guard:allow-localhost-fallback\b[^\n]*/;
|
|
32
|
+
const OPT_OUT_REQUIRES_REASON = /\/\/\s*guard:allow-localhost-fallback\s*[—-]\s*\S/;
|
|
33
|
+
const LITERAL_RE = /(?:"local@localhost"|'local@localhost'|`local@localhost`)/g;
|
|
34
|
+
const SYMBOLIC_FALLBACK_RE = /(?:\?\?|\|\|)\s*DEV_MODE_USER_EMAIL\b/g;
|
|
35
|
+
const SQL_DEFAULT_RE = /\bDEFAULT\s+['"`]local@localhost['"`]/i;
|
|
36
|
+
const DRIZZLE_DEFAULT_RE = /\.default\s*\(\s*['"`]local@localhost['"`]\s*\)/;
|
|
37
|
+
function isAllowedPath(rel, extraExemptPaths) {
|
|
38
|
+
if (extraExemptPaths?.includes(rel))
|
|
39
|
+
return true;
|
|
40
|
+
return GENERIC_ALLOWED_PATH_PREDICATES.some((p) => p(rel));
|
|
41
|
+
}
|
|
42
|
+
function hasValidOptOut(lines, lineIdx) {
|
|
43
|
+
const cur = lines[lineIdx] ?? "";
|
|
44
|
+
if (OPT_OUT_MARKER.test(cur)) {
|
|
45
|
+
return OPT_OUT_REQUIRES_REASON.test(cur);
|
|
46
|
+
}
|
|
47
|
+
const prev = lines[lineIdx - 1] ?? "";
|
|
48
|
+
if (/^\s*\/\//.test(prev) && OPT_OUT_MARKER.test(prev)) {
|
|
49
|
+
return OPT_OUT_REQUIRES_REASON.test(prev);
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
export function scanLocalhostFallback(options) {
|
|
54
|
+
const { root, extraExemptPaths } = options;
|
|
55
|
+
const findings = [];
|
|
56
|
+
for (const file of walk(root)) {
|
|
57
|
+
if (!/\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(file))
|
|
58
|
+
continue;
|
|
59
|
+
if (file.endsWith(".d.ts"))
|
|
60
|
+
continue;
|
|
61
|
+
const rel = relPosix(root, file);
|
|
62
|
+
if (isAllowedPath(rel, extraExemptPaths))
|
|
63
|
+
continue;
|
|
64
|
+
const contents = readFileSafe(file);
|
|
65
|
+
if (contents === null || !contents.includes("local@localhost"))
|
|
66
|
+
continue;
|
|
67
|
+
const lines = contents.split("\n");
|
|
68
|
+
LITERAL_RE.lastIndex = 0;
|
|
69
|
+
let m;
|
|
70
|
+
while ((m = LITERAL_RE.exec(contents)) !== null) {
|
|
71
|
+
const { line } = lineColForOffset(contents, m.index);
|
|
72
|
+
const lineText = lines[line - 1] ?? "";
|
|
73
|
+
if (isCommentLine(lineText))
|
|
74
|
+
continue;
|
|
75
|
+
if (SQL_DEFAULT_RE.test(lineText))
|
|
76
|
+
continue;
|
|
77
|
+
if (DRIZZLE_DEFAULT_RE.test(lineText))
|
|
78
|
+
continue;
|
|
79
|
+
if (hasValidOptOut(lines, line - 1))
|
|
80
|
+
continue;
|
|
81
|
+
findings.push({
|
|
82
|
+
file: rel,
|
|
83
|
+
line,
|
|
84
|
+
message: `"local@localhost" used as a fallback identity: ${lineText.trim()}. Throw/401 on missing session instead — this pools every unauthenticated request onto one shared tenant.`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
SYMBOLIC_FALLBACK_RE.lastIndex = 0;
|
|
88
|
+
let s;
|
|
89
|
+
while ((s = SYMBOLIC_FALLBACK_RE.exec(contents)) !== null) {
|
|
90
|
+
const { line } = lineColForOffset(contents, s.index);
|
|
91
|
+
const lineText = lines[line - 1] ?? "";
|
|
92
|
+
if (isCommentLine(lineText))
|
|
93
|
+
continue;
|
|
94
|
+
if (hasValidOptOut(lines, line - 1))
|
|
95
|
+
continue;
|
|
96
|
+
findings.push({
|
|
97
|
+
file: rel,
|
|
98
|
+
line,
|
|
99
|
+
message: `DEV_MODE_USER_EMAIL used as a fallback identity: ${lineText.trim()}. Throw/401 on missing session instead.`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { name: "no-localhost-fallback", findings };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=no-localhost-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-localhost-fallback.js","sourceRoot":"","sources":["../../src/guards/no-localhost-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,IAAI,GACL,MAAM,iBAAiB,CAAC;AASzB,MAAM,+BAA+B,GAAoC;IACvE,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;IACrC,CAAC,GAAG,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;IACrC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/B,CAAC,GAAG,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;IAC7B,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;CAC/B,CAAC;AAEF,MAAM,cAAc,GAAG,+CAA+C,CAAC;AACvE,MAAM,uBAAuB,GAC3B,mDAAmD,CAAC;AAEtD,MAAM,UAAU,GAAG,4DAA4D,CAAC;AAEhF,MAAM,oBAAoB,GAAG,wCAAwC,CAAC;AAEtE,MAAM,cAAc,GAAG,wCAAwC,CAAC;AAChE,MAAM,kBAAkB,GAAG,iDAAiD,CAAC;AAE7E,SAAS,aAAa,CAAC,GAAW,EAAE,gBAA2B;IAC7D,IAAI,gBAAgB,EAAE,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACjD,OAAO,+BAA+B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,cAAc,CAAC,KAAe,EAAE,OAAe;IACtD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvD,OAAO,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAAiC;IAEjC,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;IAC3C,MAAM,QAAQ,GAAmB,EAAE,CAAC;IAEpC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,gCAAgC,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAC3D,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,SAAS;QACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjC,IAAI,aAAa,CAAC,GAAG,EAAE,gBAAgB,CAAC;YAAE,SAAS;QAEnD,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAAE,SAAS;QAEzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAEnC,UAAU,CAAC,SAAS,GAAG,CAAC,CAAC;QACzB,IAAI,CAAyB,CAAC;QAC9B,OAAO,CAAC,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAChD,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YACrD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACvC,IAAI,aAAa,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACtC,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAC5C,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAChD,IAAI,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC;gBAAE,SAAS;YAC9C,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,GAAG;gBACT,IAAI;gBACJ,OAAO,EAAE,kDAAkD,QAAQ,CAAC,IAAI,EAAE,2GAA2G;aACtL,CAAC,CAAC;QACL,CAAC;QAED,oBAAoB,CAAC,SAAS,GAAG,CAAC,CAAC;QACnC,IAAI,CAAyB,CAAC;QAC9B,OAAO,CAAC,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1D,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;YACrD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACvC,IAAI,aAAa,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACtC,IAAI,cAAc,CAAC,KAAK,EAAE,IAAI,GAAG,CAAC,CAAC;gBAAE,SAAS;YAC9C,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,GAAG;gBACT,IAAI;gBACJ,OAAO,EAAE,oDAAoD,QAAQ,CAAC,IAAI,EAAE,yCAAyC;aACtH,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,QAAQ,EAAE,CAAC;AACrD,CAAC","sourcesContent":["/**\n * scanLocalhostFallback — ported from\n * `scripts/guard-no-localhost-fallback.mjs`.\n *\n * Refuse to let production code use the literal `local@localhost` (or the\n * `DEV_MODE_USER_EMAIL` symbolic alias) as a fallback identity when no\n * session is present — that pools every unauthenticated request onto one\n * shared tenant. The right behavior in production is to throw/401 when\n * there's no session, not fall back to a sentinel identity.\n *\n * Conditional guard — per report 005's V1 guard set table: the original\n * hardcodes framework-file exemptions (`packages/core/src/server/auth.ts`,\n * `packages/core/src/org/context.ts`, etc. — files that either define or\n * intentionally handle the dev-mode sentinel). Those live in `node_modules`\n * for a generated app and are already excluded by `SKIP_DIRS`, so they're\n * moved behind an optional `extraExemptPaths` param (default `[]`) instead\n * of hardcoded. The generic allowlist predicates (`*.spec.*`, `*.test.*`,\n * `scripts/**`, `**\\/seed(s)/**`) are kept verbatim.\n *\n * Opt-out (same line, or the line immediately above):\n * const x = email ?? \"local@localhost\" // guard:allow-localhost-fallback — short reason\n */\n\nimport {\n isCommentLine,\n lineColForOffset,\n readFileSafe,\n relPosix,\n walk,\n} from \"./scan-utils.js\";\nimport type { GuardFinding, GuardResult, GuardScanOptions } from \"./types.js\";\n\nexport interface LocalhostFallbackOptions extends GuardScanOptions {\n /** Exact repo-relative paths to exempt in addition to the generic\n * predicates (spec/test/scripts/seed). Default `[]`. */\n extraExemptPaths?: string[];\n}\n\nconst GENERIC_ALLOWED_PATH_PREDICATES: Array<(rel: string) => boolean> = [\n (rel) => /\\.spec\\.[tj]sx?$/.test(rel),\n (rel) => /\\.test\\.[tj]sx?$/.test(rel),\n (rel) => /^scripts\\//.test(rel),\n (rel) => /\\/seed\\//.test(rel),\n (rel) => /\\/seeds\\//.test(rel),\n];\n\nconst OPT_OUT_MARKER = /\\/\\/\\s*guard:allow-localhost-fallback\\b[^\\n]*/;\nconst OPT_OUT_REQUIRES_REASON =\n /\\/\\/\\s*guard:allow-localhost-fallback\\s*[—-]\\s*\\S/;\n\nconst LITERAL_RE = /(?:\"local@localhost\"|'local@localhost'|`local@localhost`)/g;\n\nconst SYMBOLIC_FALLBACK_RE = /(?:\\?\\?|\\|\\|)\\s*DEV_MODE_USER_EMAIL\\b/g;\n\nconst SQL_DEFAULT_RE = /\\bDEFAULT\\s+['\"`]local@localhost['\"`]/i;\nconst DRIZZLE_DEFAULT_RE = /\\.default\\s*\\(\\s*['\"`]local@localhost['\"`]\\s*\\)/;\n\nfunction isAllowedPath(rel: string, extraExemptPaths?: string[]): boolean {\n if (extraExemptPaths?.includes(rel)) return true;\n return GENERIC_ALLOWED_PATH_PREDICATES.some((p) => p(rel));\n}\n\nfunction hasValidOptOut(lines: string[], lineIdx: number): boolean {\n const cur = lines[lineIdx] ?? \"\";\n if (OPT_OUT_MARKER.test(cur)) {\n return OPT_OUT_REQUIRES_REASON.test(cur);\n }\n const prev = lines[lineIdx - 1] ?? \"\";\n if (/^\\s*\\/\\//.test(prev) && OPT_OUT_MARKER.test(prev)) {\n return OPT_OUT_REQUIRES_REASON.test(prev);\n }\n return false;\n}\n\nexport function scanLocalhostFallback(\n options: LocalhostFallbackOptions,\n): GuardResult {\n const { root, extraExemptPaths } = options;\n const findings: GuardFinding[] = [];\n\n for (const file of walk(root)) {\n if (!/\\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(file)) continue;\n if (file.endsWith(\".d.ts\")) continue;\n const rel = relPosix(root, file);\n if (isAllowedPath(rel, extraExemptPaths)) continue;\n\n const contents = readFileSafe(file);\n if (contents === null || !contents.includes(\"local@localhost\")) continue;\n\n const lines = contents.split(\"\\n\");\n\n LITERAL_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = LITERAL_RE.exec(contents)) !== null) {\n const { line } = lineColForOffset(contents, m.index);\n const lineText = lines[line - 1] ?? \"\";\n if (isCommentLine(lineText)) continue;\n if (SQL_DEFAULT_RE.test(lineText)) continue;\n if (DRIZZLE_DEFAULT_RE.test(lineText)) continue;\n if (hasValidOptOut(lines, line - 1)) continue;\n findings.push({\n file: rel,\n line,\n message: `\"local@localhost\" used as a fallback identity: ${lineText.trim()}. Throw/401 on missing session instead — this pools every unauthenticated request onto one shared tenant.`,\n });\n }\n\n SYMBOLIC_FALLBACK_RE.lastIndex = 0;\n let s: RegExpExecArray | null;\n while ((s = SYMBOLIC_FALLBACK_RE.exec(contents)) !== null) {\n const { line } = lineColForOffset(contents, s.index);\n const lineText = lines[line - 1] ?? \"\";\n if (isCommentLine(lineText)) continue;\n if (hasValidOptOut(lines, line - 1)) continue;\n findings.push({\n file: rel,\n line,\n message: `DEV_MODE_USER_EMAIL used as a fallback identity: ${lineText.trim()}. Throw/401 on missing session instead.`,\n });\n }\n }\n\n return { name: \"no-localhost-fallback\", findings };\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanUnscopedCredentials — ported from
|
|
3
|
+
* `scripts/guard-no-unscoped-credentials.mjs`.
|
|
4
|
+
*
|
|
5
|
+
* Refuse to let any caller invoke `resolveCredential` / `hasCredential` /
|
|
6
|
+
* `saveCredential` / `deleteCredential` with only one argument. The
|
|
7
|
+
* contract requires a second argument carrying the per-user / per-org
|
|
8
|
+
* context object — `(key, { userEmail, orgId })` — so the credential
|
|
9
|
+
* lookup is scoped to the requesting principal.
|
|
10
|
+
*
|
|
11
|
+
* Generic guard, shipped as-is: the detection regex, arg-balancing parser,
|
|
12
|
+
* and marker semantics are unchanged from the monorepo original. The one
|
|
13
|
+
* difference from the monorepo script is scan scope: the original restricts
|
|
14
|
+
* its walk to `packages/`, `templates/`, `app/` — a monorepo-specific
|
|
15
|
+
* optimization that also happens to exclude its own dotfile/doc trees. A
|
|
16
|
+
* generated app's source lives at `actions/`, `app/`, `server/` (no
|
|
17
|
+
* `packages/`/`templates/` wrapper — see report 005's "Path resolution"),
|
|
18
|
+
* so keeping that literal subtree filter would silently skip `actions/`
|
|
19
|
+
* and `server/`. This port scans the whole app root (still respecting
|
|
20
|
+
* `SKIP_DIRS`) instead, which is a superset of the original's coverage and
|
|
21
|
+
* has no false-negative risk for a single-app tree.
|
|
22
|
+
*
|
|
23
|
+
* Opt-out (same line only):
|
|
24
|
+
* resolveCredential(key) // guard:allow-unscoped-credential — short reason
|
|
25
|
+
*/
|
|
26
|
+
import type { GuardResult, GuardScanOptions } from "./types.js";
|
|
27
|
+
export declare function scanUnscopedCredentials(options: GuardScanOptions): GuardResult;
|
|
28
|
+
//# sourceMappingURL=no-unscoped-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unscoped-credentials.d.ts","sourceRoot":"","sources":["../../src/guards/no-unscoped-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAQH,OAAO,KAAK,EAAgB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAqI9E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,gBAAgB,GACxB,WAAW,CA2Db"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanUnscopedCredentials — ported from
|
|
3
|
+
* `scripts/guard-no-unscoped-credentials.mjs`.
|
|
4
|
+
*
|
|
5
|
+
* Refuse to let any caller invoke `resolveCredential` / `hasCredential` /
|
|
6
|
+
* `saveCredential` / `deleteCredential` with only one argument. The
|
|
7
|
+
* contract requires a second argument carrying the per-user / per-org
|
|
8
|
+
* context object — `(key, { userEmail, orgId })` — so the credential
|
|
9
|
+
* lookup is scoped to the requesting principal.
|
|
10
|
+
*
|
|
11
|
+
* Generic guard, shipped as-is: the detection regex, arg-balancing parser,
|
|
12
|
+
* and marker semantics are unchanged from the monorepo original. The one
|
|
13
|
+
* difference from the monorepo script is scan scope: the original restricts
|
|
14
|
+
* its walk to `packages/`, `templates/`, `app/` — a monorepo-specific
|
|
15
|
+
* optimization that also happens to exclude its own dotfile/doc trees. A
|
|
16
|
+
* generated app's source lives at `actions/`, `app/`, `server/` (no
|
|
17
|
+
* `packages/`/`templates/` wrapper — see report 005's "Path resolution"),
|
|
18
|
+
* so keeping that literal subtree filter would silently skip `actions/`
|
|
19
|
+
* and `server/`. This port scans the whole app root (still respecting
|
|
20
|
+
* `SKIP_DIRS`) instead, which is a superset of the original's coverage and
|
|
21
|
+
* has no false-negative risk for a single-app tree.
|
|
22
|
+
*
|
|
23
|
+
* Opt-out (same line only):
|
|
24
|
+
* resolveCredential(key) // guard:allow-unscoped-credential — short reason
|
|
25
|
+
*/
|
|
26
|
+
import { lineColForOffset, readFileSafe, relPosix, walk, } from "./scan-utils.js";
|
|
27
|
+
const FUNCTIONS = [
|
|
28
|
+
"resolveCredential",
|
|
29
|
+
"hasCredential",
|
|
30
|
+
"saveCredential",
|
|
31
|
+
"deleteCredential",
|
|
32
|
+
];
|
|
33
|
+
const FUNC_NAME_RE = new RegExp(`(?<![\\w$.])(${FUNCTIONS.join("|")})\\s*\\(`, "g");
|
|
34
|
+
const OPT_OUT_MARKER = /\/\/\s*guard:allow-unscoped-credential\b[^\n]*[—-]\s*\S/;
|
|
35
|
+
/** Files that legitimately use the one-arg form because they ARE the
|
|
36
|
+
* implementation under guard — the definition file itself. In an app this
|
|
37
|
+
* file only ever exists inside `node_modules`, already excluded by
|
|
38
|
+
* SKIP_DIRS, so this allowlist is effectively a no-op for app mode; kept
|
|
39
|
+
* for parity with the source guard. */
|
|
40
|
+
const FILE_ALLOWLIST = new Set(["packages/core/src/credentials/index.ts"]);
|
|
41
|
+
/**
|
|
42
|
+
* Starting at openParenIdx (the index of the `(` after the function name),
|
|
43
|
+
* walk forward, balancing nested parens / braces / brackets / strings, and
|
|
44
|
+
* return the top-level comma count. Ported verbatim.
|
|
45
|
+
*/
|
|
46
|
+
function analyzeArgs(contents, openParenIdx) {
|
|
47
|
+
let depth = 0;
|
|
48
|
+
let topLevelCommas = 0;
|
|
49
|
+
let i = openParenIdx;
|
|
50
|
+
let mode = "code";
|
|
51
|
+
let templateDepth = 0;
|
|
52
|
+
while (i < contents.length) {
|
|
53
|
+
const ch = contents[i];
|
|
54
|
+
const next = contents[i + 1];
|
|
55
|
+
if (mode === "code") {
|
|
56
|
+
if (ch === "/" && next === "/") {
|
|
57
|
+
while (i < contents.length && contents[i] !== "\n")
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (ch === "/" && next === "*") {
|
|
62
|
+
i += 2;
|
|
63
|
+
while (i < contents.length - 1 &&
|
|
64
|
+
!(contents[i] === "*" && contents[i + 1] === "/"))
|
|
65
|
+
i++;
|
|
66
|
+
i += 2;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (ch === '"' || ch === "'") {
|
|
70
|
+
mode = ch;
|
|
71
|
+
i++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (ch === "`") {
|
|
75
|
+
mode = "tmpl";
|
|
76
|
+
i++;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (ch === "(" || ch === "[" || ch === "{") {
|
|
80
|
+
depth++;
|
|
81
|
+
i++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (ch === ")" || ch === "]" || ch === "}") {
|
|
85
|
+
depth--;
|
|
86
|
+
if (depth === 0 && ch === ")") {
|
|
87
|
+
return { topLevelCommas, endIdx: i };
|
|
88
|
+
}
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (ch === "," && depth === 1) {
|
|
93
|
+
topLevelCommas++;
|
|
94
|
+
i++;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
i++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (mode === '"' || mode === "'") {
|
|
101
|
+
if (ch === "\\") {
|
|
102
|
+
i += 2;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (ch === mode) {
|
|
106
|
+
mode = "code";
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
i++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (mode === "tmpl") {
|
|
114
|
+
if (ch === "\\") {
|
|
115
|
+
i += 2;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (ch === "`" && templateDepth === 0) {
|
|
119
|
+
mode = "code";
|
|
120
|
+
i++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (ch === "$" && next === "{") {
|
|
124
|
+
templateDepth++;
|
|
125
|
+
i += 2;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (ch === "}" && templateDepth > 0) {
|
|
129
|
+
templateDepth--;
|
|
130
|
+
i++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
i++;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
export function scanUnscopedCredentials(options) {
|
|
141
|
+
const { root } = options;
|
|
142
|
+
const findings = [];
|
|
143
|
+
for (const file of walk(root)) {
|
|
144
|
+
if (!/\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(file))
|
|
145
|
+
continue;
|
|
146
|
+
if (file.endsWith(".d.ts"))
|
|
147
|
+
continue;
|
|
148
|
+
const rel = relPosix(root, file);
|
|
149
|
+
if (FILE_ALLOWLIST.has(rel))
|
|
150
|
+
continue;
|
|
151
|
+
const contents = readFileSafe(file);
|
|
152
|
+
if (contents === null)
|
|
153
|
+
continue;
|
|
154
|
+
let anyHit = false;
|
|
155
|
+
for (const fn of FUNCTIONS) {
|
|
156
|
+
if (contents.includes(fn + "(") || contents.includes(fn + " (")) {
|
|
157
|
+
anyHit = true;
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (!anyHit)
|
|
162
|
+
continue;
|
|
163
|
+
const lines = contents.split("\n");
|
|
164
|
+
FUNC_NAME_RE.lastIndex = 0;
|
|
165
|
+
let m;
|
|
166
|
+
while ((m = FUNC_NAME_RE.exec(contents)) !== null) {
|
|
167
|
+
const fnName = m[1];
|
|
168
|
+
const openParenIdx = m.index + m[0].length - 1;
|
|
169
|
+
const before = contents.slice(Math.max(0, m.index - 30), m.index);
|
|
170
|
+
if (/\bfunction\s+$/.test(before) ||
|
|
171
|
+
/\bexport\s+(default\s+)?(async\s+)?function\s+$/.test(before) ||
|
|
172
|
+
/\b(const|let|var)\s+$/.test(before)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const { line } = lineColForOffset(contents, m.index);
|
|
176
|
+
const lineText = lines[line - 1] ?? "";
|
|
177
|
+
const trimmed = lineText.trimStart();
|
|
178
|
+
if (trimmed.startsWith("*") ||
|
|
179
|
+
trimmed.startsWith("//") ||
|
|
180
|
+
trimmed.startsWith("/*")) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const analysis = analyzeArgs(contents, openParenIdx);
|
|
184
|
+
if (!analysis)
|
|
185
|
+
continue;
|
|
186
|
+
if (analysis.topLevelCommas >= 1)
|
|
187
|
+
continue;
|
|
188
|
+
if (OPT_OUT_MARKER.test(lineText))
|
|
189
|
+
continue;
|
|
190
|
+
findings.push({
|
|
191
|
+
file: rel,
|
|
192
|
+
line,
|
|
193
|
+
message: `${fnName}(...) called with only one argument — must pass a context object: ${fnName}(key, { userEmail, orgId })`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { name: "no-unscoped-credentials", findings };
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=no-unscoped-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unscoped-credentials.js","sourceRoot":"","sources":["../../src/guards/no-unscoped-credentials.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * scanUnscopedCredentials — ported from\n * `scripts/guard-no-unscoped-credentials.mjs`.\n *\n * Refuse to let any caller invoke `resolveCredential` / `hasCredential` /\n * `saveCredential` / `deleteCredential` with only one argument. The\n * contract requires a second argument carrying the per-user / per-org\n * context object — `(key, { userEmail, orgId })` — so the credential\n * lookup is scoped to the requesting principal.\n *\n * Generic guard, shipped as-is: the detection regex, arg-balancing parser,\n * and marker semantics are unchanged from the monorepo original. The one\n * difference from the monorepo script is scan scope: the original restricts\n * its walk to `packages/`, `templates/`, `app/` — a monorepo-specific\n * optimization that also happens to exclude its own dotfile/doc trees. A\n * generated app's source lives at `actions/`, `app/`, `server/` (no\n * `packages/`/`templates/` wrapper — see report 005's \"Path resolution\"),\n * so keeping that literal subtree filter would silently skip `actions/`\n * and `server/`. This port scans the whole app root (still respecting\n * `SKIP_DIRS`) instead, which is a superset of the original's coverage and\n * has no false-negative risk for a single-app tree.\n *\n * Opt-out (same line only):\n * resolveCredential(key) // guard:allow-unscoped-credential — short reason\n */\n\nimport {\n lineColForOffset,\n readFileSafe,\n relPosix,\n walk,\n} from \"./scan-utils.js\";\nimport type { GuardFinding, GuardResult, GuardScanOptions } from \"./types.js\";\n\nconst FUNCTIONS = [\n \"resolveCredential\",\n \"hasCredential\",\n \"saveCredential\",\n \"deleteCredential\",\n];\n\nconst FUNC_NAME_RE = new RegExp(\n `(?<![\\\\w$.])(${FUNCTIONS.join(\"|\")})\\\\s*\\\\(`,\n \"g\",\n);\n\nconst OPT_OUT_MARKER =\n /\\/\\/\\s*guard:allow-unscoped-credential\\b[^\\n]*[—-]\\s*\\S/;\n\n/** Files that legitimately use the one-arg form because they ARE the\n * implementation under guard — the definition file itself. In an app this\n * file only ever exists inside `node_modules`, already excluded by\n * SKIP_DIRS, so this allowlist is effectively a no-op for app mode; kept\n * for parity with the source guard. */\nconst FILE_ALLOWLIST = new Set([\"packages/core/src/credentials/index.ts\"]);\n\ninterface ArgAnalysis {\n topLevelCommas: number;\n endIdx: number;\n}\n\n/**\n * Starting at openParenIdx (the index of the `(` after the function name),\n * walk forward, balancing nested parens / braces / brackets / strings, and\n * return the top-level comma count. Ported verbatim.\n */\nfunction analyzeArgs(\n contents: string,\n openParenIdx: number,\n): ArgAnalysis | null {\n let depth = 0;\n let topLevelCommas = 0;\n let i = openParenIdx;\n let mode: \"code\" | '\"' | \"'\" | \"tmpl\" = \"code\";\n let templateDepth = 0;\n while (i < contents.length) {\n const ch = contents[i];\n const next = contents[i + 1];\n if (mode === \"code\") {\n if (ch === \"/\" && next === \"/\") {\n while (i < contents.length && contents[i] !== \"\\n\") i++;\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n i += 2;\n while (\n i < contents.length - 1 &&\n !(contents[i] === \"*\" && contents[i + 1] === \"/\")\n )\n i++;\n i += 2;\n continue;\n }\n if (ch === '\"' || ch === \"'\") {\n mode = ch;\n i++;\n continue;\n }\n if (ch === \"`\") {\n mode = \"tmpl\";\n i++;\n continue;\n }\n if (ch === \"(\" || ch === \"[\" || ch === \"{\") {\n depth++;\n i++;\n continue;\n }\n if (ch === \")\" || ch === \"]\" || ch === \"}\") {\n depth--;\n if (depth === 0 && ch === \")\") {\n return { topLevelCommas, endIdx: i };\n }\n i++;\n continue;\n }\n if (ch === \",\" && depth === 1) {\n topLevelCommas++;\n i++;\n continue;\n }\n i++;\n continue;\n }\n if (mode === '\"' || mode === \"'\") {\n if (ch === \"\\\\\") {\n i += 2;\n continue;\n }\n if (ch === mode) {\n mode = \"code\";\n i++;\n continue;\n }\n i++;\n continue;\n }\n if (mode === \"tmpl\") {\n if (ch === \"\\\\\") {\n i += 2;\n continue;\n }\n if (ch === \"`\" && templateDepth === 0) {\n mode = \"code\";\n i++;\n continue;\n }\n if (ch === \"$\" && next === \"{\") {\n templateDepth++;\n i += 2;\n continue;\n }\n if (ch === \"}\" && templateDepth > 0) {\n templateDepth--;\n i++;\n continue;\n }\n i++;\n continue;\n }\n i++;\n }\n return null;\n}\n\nexport function scanUnscopedCredentials(\n options: GuardScanOptions,\n): GuardResult {\n const { root } = options;\n const findings: GuardFinding[] = [];\n\n for (const file of walk(root)) {\n if (!/\\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(file)) continue;\n if (file.endsWith(\".d.ts\")) continue;\n const rel = relPosix(root, file);\n if (FILE_ALLOWLIST.has(rel)) continue;\n\n const contents = readFileSafe(file);\n if (contents === null) continue;\n\n let anyHit = false;\n for (const fn of FUNCTIONS) {\n if (contents.includes(fn + \"(\") || contents.includes(fn + \" (\")) {\n anyHit = true;\n break;\n }\n }\n if (!anyHit) continue;\n\n const lines = contents.split(\"\\n\");\n FUNC_NAME_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = FUNC_NAME_RE.exec(contents)) !== null) {\n const fnName = m[1];\n const openParenIdx = m.index + m[0].length - 1;\n const before = contents.slice(Math.max(0, m.index - 30), m.index);\n if (\n /\\bfunction\\s+$/.test(before) ||\n /\\bexport\\s+(default\\s+)?(async\\s+)?function\\s+$/.test(before) ||\n /\\b(const|let|var)\\s+$/.test(before)\n ) {\n continue;\n }\n const { line } = lineColForOffset(contents, m.index);\n const lineText = lines[line - 1] ?? \"\";\n const trimmed = lineText.trimStart();\n if (\n trimmed.startsWith(\"*\") ||\n trimmed.startsWith(\"//\") ||\n trimmed.startsWith(\"/*\")\n ) {\n continue;\n }\n const analysis = analyzeArgs(contents, openParenIdx);\n if (!analysis) continue;\n if (analysis.topLevelCommas >= 1) continue;\n if (OPT_OUT_MARKER.test(lineText)) continue;\n findings.push({\n file: rel,\n line,\n message: `${fnName}(...) called with only one argument — must pass a context object: ${fnName}(key, { userEmail, orgId })`,\n });\n }\n }\n\n return { name: \"no-unscoped-credentials\", findings };\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scanUnscopedQueries — ported from `scripts/guard-no-unscoped-queries.mjs`.
|
|
3
|
+
*
|
|
4
|
+
* Refuse to let any code query an "ownable" resource table (one that
|
|
5
|
+
* spreads `...ownableColumns()`) without going through framework access
|
|
6
|
+
* control: `accessFilter` / `resolveAccess` / `assertAccess`, or an
|
|
7
|
+
* explicit `ownerEmail` / `userEmail` / `orgId` filter. The check is
|
|
8
|
+
* per-STATEMENT, scoped to the enclosing block, so a sibling `if` branch
|
|
9
|
+
* that correctly scopes its query does not defuse an unscoped sibling.
|
|
10
|
+
*
|
|
11
|
+
* Conditional guard — per report 005's V1 guard set table: the original
|
|
12
|
+
* restricts scanning to `templates/[^/]+/(server|actions)/` and
|
|
13
|
+
* `packages/[^/]+/src/`, and hardcodes a `FILE_ALLOWLIST` of this
|
|
14
|
+
* framework's own sharing-primitive implementation files. For a generated
|
|
15
|
+
* app (no `templates/`/`packages/` wrapper — source lives at `actions/`
|
|
16
|
+
* and `server/` under the app root), this port scans `actions/` and
|
|
17
|
+
* `server/` directly and drops the hardcoded allowlist in favor of an
|
|
18
|
+
* optional `extraExemptPaths` param (default `[]`).
|
|
19
|
+
*
|
|
20
|
+
* Opt-out (within the enclosing block, or as a file header for whole-file
|
|
21
|
+
* opt-outs):
|
|
22
|
+
* // guard:allow-unscoped — short reason
|
|
23
|
+
*/
|
|
24
|
+
import type { GuardResult, GuardScanOptions } from "./types.js";
|
|
25
|
+
export interface UnscopedQueriesOptions extends GuardScanOptions {
|
|
26
|
+
/** Exact repo-relative paths to exempt from the per-statement scan
|
|
27
|
+
* (e.g. this app's own sharing-primitive implementation, if any).
|
|
28
|
+
* Default `[]`. */
|
|
29
|
+
extraExemptPaths?: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare function scanUnscopedQueries(options: UnscopedQueriesOptions): GuardResult;
|
|
32
|
+
//# sourceMappingURL=no-unscoped-queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unscoped-queries.d.ts","sourceRoot":"","sources":["../../src/guards/no-unscoped-queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAgB,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D;;uBAEmB;IACnB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AA6vBD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,sBAAsB,GAC9B,WAAW,CAkCb"}
|