palaryn 0.1.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/LICENSE +21 -0
- package/README.md +716 -0
- package/dist/sdk/typescript/src/client.d.ts +71 -0
- package/dist/sdk/typescript/src/client.d.ts.map +1 -0
- package/dist/sdk/typescript/src/client.js +176 -0
- package/dist/sdk/typescript/src/client.js.map +1 -0
- package/dist/sdk/typescript/src/errors.d.ts +50 -0
- package/dist/sdk/typescript/src/errors.d.ts.map +1 -0
- package/dist/sdk/typescript/src/errors.js +103 -0
- package/dist/sdk/typescript/src/errors.js.map +1 -0
- package/dist/sdk/typescript/src/index.d.ts +4 -0
- package/dist/sdk/typescript/src/index.d.ts.map +1 -0
- package/dist/sdk/typescript/src/index.js +15 -0
- package/dist/sdk/typescript/src/index.js.map +1 -0
- package/dist/sdk/typescript/src/types.d.ts +101 -0
- package/dist/sdk/typescript/src/types.d.ts.map +1 -0
- package/dist/sdk/typescript/src/types.js +6 -0
- package/dist/sdk/typescript/src/types.js.map +1 -0
- package/dist/src/admin/index.d.ts +2 -0
- package/dist/src/admin/index.d.ts.map +1 -0
- package/dist/src/admin/index.js +6 -0
- package/dist/src/admin/index.js.map +1 -0
- package/dist/src/admin/routes.d.ts +5 -0
- package/dist/src/admin/routes.d.ts.map +1 -0
- package/dist/src/admin/routes.js +471 -0
- package/dist/src/admin/routes.js.map +1 -0
- package/dist/src/admin/templates.d.ts +51 -0
- package/dist/src/admin/templates.d.ts.map +1 -0
- package/dist/src/admin/templates.js +500 -0
- package/dist/src/admin/templates.js.map +1 -0
- package/dist/src/anomaly/detector.d.ts +141 -0
- package/dist/src/anomaly/detector.d.ts.map +1 -0
- package/dist/src/anomaly/detector.js +554 -0
- package/dist/src/anomaly/detector.js.map +1 -0
- package/dist/src/anomaly/index.d.ts +2 -0
- package/dist/src/anomaly/index.d.ts.map +1 -0
- package/dist/src/anomaly/index.js +7 -0
- package/dist/src/anomaly/index.js.map +1 -0
- package/dist/src/approval/manager.d.ts +147 -0
- package/dist/src/approval/manager.d.ts.map +1 -0
- package/dist/src/approval/manager.js +511 -0
- package/dist/src/approval/manager.js.map +1 -0
- package/dist/src/approval/webhook.d.ts +36 -0
- package/dist/src/approval/webhook.d.ts.map +1 -0
- package/dist/src/approval/webhook.js +135 -0
- package/dist/src/approval/webhook.js.map +1 -0
- package/dist/src/audit/logger.d.ts +70 -0
- package/dist/src/audit/logger.d.ts.map +1 -0
- package/dist/src/audit/logger.js +440 -0
- package/dist/src/audit/logger.js.map +1 -0
- package/dist/src/auth/index.d.ts +6 -0
- package/dist/src/auth/index.d.ts.map +1 -0
- package/dist/src/auth/index.js +22 -0
- package/dist/src/auth/index.js.map +1 -0
- package/dist/src/auth/password.d.ts +3 -0
- package/dist/src/auth/password.d.ts.map +1 -0
- package/dist/src/auth/password.js +25 -0
- package/dist/src/auth/password.js.map +1 -0
- package/dist/src/auth/pkce.d.ts +13 -0
- package/dist/src/auth/pkce.d.ts.map +1 -0
- package/dist/src/auth/pkce.js +58 -0
- package/dist/src/auth/pkce.js.map +1 -0
- package/dist/src/auth/providers.d.ts +28 -0
- package/dist/src/auth/providers.d.ts.map +1 -0
- package/dist/src/auth/providers.js +198 -0
- package/dist/src/auth/providers.js.map +1 -0
- package/dist/src/auth/routes.d.ts +14 -0
- package/dist/src/auth/routes.d.ts.map +1 -0
- package/dist/src/auth/routes.js +431 -0
- package/dist/src/auth/routes.js.map +1 -0
- package/dist/src/auth/session.d.ts +24 -0
- package/dist/src/auth/session.d.ts.map +1 -0
- package/dist/src/auth/session.js +105 -0
- package/dist/src/auth/session.js.map +1 -0
- package/dist/src/billing/index.d.ts +7 -0
- package/dist/src/billing/index.d.ts.map +1 -0
- package/dist/src/billing/index.js +14 -0
- package/dist/src/billing/index.js.map +1 -0
- package/dist/src/billing/plan-enforcer.d.ts +44 -0
- package/dist/src/billing/plan-enforcer.d.ts.map +1 -0
- package/dist/src/billing/plan-enforcer.js +110 -0
- package/dist/src/billing/plan-enforcer.js.map +1 -0
- package/dist/src/billing/routes.d.ts +15 -0
- package/dist/src/billing/routes.d.ts.map +1 -0
- package/dist/src/billing/routes.js +193 -0
- package/dist/src/billing/routes.js.map +1 -0
- package/dist/src/billing/stripe-client.d.ts +14 -0
- package/dist/src/billing/stripe-client.d.ts.map +1 -0
- package/dist/src/billing/stripe-client.js +51 -0
- package/dist/src/billing/stripe-client.js.map +1 -0
- package/dist/src/billing/webhook-handler.d.ts +19 -0
- package/dist/src/billing/webhook-handler.d.ts.map +1 -0
- package/dist/src/billing/webhook-handler.js +169 -0
- package/dist/src/billing/webhook-handler.js.map +1 -0
- package/dist/src/billing/webhook-routes.d.ts +5 -0
- package/dist/src/billing/webhook-routes.d.ts.map +1 -0
- package/dist/src/billing/webhook-routes.js +30 -0
- package/dist/src/billing/webhook-routes.js.map +1 -0
- package/dist/src/budget/manager.d.ts +95 -0
- package/dist/src/budget/manager.d.ts.map +1 -0
- package/dist/src/budget/manager.js +547 -0
- package/dist/src/budget/manager.js.map +1 -0
- package/dist/src/budget/usage-extractor.d.ts +38 -0
- package/dist/src/budget/usage-extractor.d.ts.map +1 -0
- package/dist/src/budget/usage-extractor.js +165 -0
- package/dist/src/budget/usage-extractor.js.map +1 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +115 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config/defaults.d.ts +3 -0
- package/dist/src/config/defaults.d.ts.map +1 -0
- package/dist/src/config/defaults.js +243 -0
- package/dist/src/config/defaults.js.map +1 -0
- package/dist/src/config/validate.d.ts +15 -0
- package/dist/src/config/validate.d.ts.map +1 -0
- package/dist/src/config/validate.js +105 -0
- package/dist/src/config/validate.js.map +1 -0
- package/dist/src/dlp/composite-scanner.d.ts +47 -0
- package/dist/src/dlp/composite-scanner.d.ts.map +1 -0
- package/dist/src/dlp/composite-scanner.js +186 -0
- package/dist/src/dlp/composite-scanner.js.map +1 -0
- package/dist/src/dlp/index.d.ts +10 -0
- package/dist/src/dlp/index.d.ts.map +1 -0
- package/dist/src/dlp/index.js +26 -0
- package/dist/src/dlp/index.js.map +1 -0
- package/dist/src/dlp/interfaces.d.ts +33 -0
- package/dist/src/dlp/interfaces.d.ts.map +1 -0
- package/dist/src/dlp/interfaces.js +3 -0
- package/dist/src/dlp/interfaces.js.map +1 -0
- package/dist/src/dlp/patterns.d.ts +9 -0
- package/dist/src/dlp/patterns.d.ts.map +1 -0
- package/dist/src/dlp/patterns.js +25 -0
- package/dist/src/dlp/patterns.js.map +1 -0
- package/dist/src/dlp/prompt-injection-backend.d.ts +68 -0
- package/dist/src/dlp/prompt-injection-backend.d.ts.map +1 -0
- package/dist/src/dlp/prompt-injection-backend.js +148 -0
- package/dist/src/dlp/prompt-injection-backend.js.map +1 -0
- package/dist/src/dlp/prompt-injection-patterns.d.ts +32 -0
- package/dist/src/dlp/prompt-injection-patterns.d.ts.map +1 -0
- package/dist/src/dlp/prompt-injection-patterns.js +290 -0
- package/dist/src/dlp/prompt-injection-patterns.js.map +1 -0
- package/dist/src/dlp/regex-backend.d.ts +32 -0
- package/dist/src/dlp/regex-backend.d.ts.map +1 -0
- package/dist/src/dlp/regex-backend.js +153 -0
- package/dist/src/dlp/regex-backend.js.map +1 -0
- package/dist/src/dlp/scanner.d.ts +122 -0
- package/dist/src/dlp/scanner.d.ts.map +1 -0
- package/dist/src/dlp/scanner.js +444 -0
- package/dist/src/dlp/scanner.js.map +1 -0
- package/dist/src/dlp/text-normalizer.d.ts +41 -0
- package/dist/src/dlp/text-normalizer.d.ts.map +1 -0
- package/dist/src/dlp/text-normalizer.js +203 -0
- package/dist/src/dlp/text-normalizer.js.map +1 -0
- package/dist/src/dlp/trufflehog-backend.d.ts +64 -0
- package/dist/src/dlp/trufflehog-backend.d.ts.map +1 -0
- package/dist/src/dlp/trufflehog-backend.js +151 -0
- package/dist/src/dlp/trufflehog-backend.js.map +1 -0
- package/dist/src/executor/http-executor.d.ts +25 -0
- package/dist/src/executor/http-executor.d.ts.map +1 -0
- package/dist/src/executor/http-executor.js +333 -0
- package/dist/src/executor/http-executor.js.map +1 -0
- package/dist/src/executor/index.d.ts +6 -0
- package/dist/src/executor/index.d.ts.map +1 -0
- package/dist/src/executor/index.js +12 -0
- package/dist/src/executor/index.js.map +1 -0
- package/dist/src/executor/interfaces.d.ts +11 -0
- package/dist/src/executor/interfaces.d.ts.map +1 -0
- package/dist/src/executor/interfaces.js +3 -0
- package/dist/src/executor/interfaces.js.map +1 -0
- package/dist/src/executor/noop-executor.d.ts +13 -0
- package/dist/src/executor/noop-executor.d.ts.map +1 -0
- package/dist/src/executor/noop-executor.js +21 -0
- package/dist/src/executor/noop-executor.js.map +1 -0
- package/dist/src/executor/registry.d.ts +30 -0
- package/dist/src/executor/registry.d.ts.map +1 -0
- package/dist/src/executor/registry.js +62 -0
- package/dist/src/executor/registry.js.map +1 -0
- package/dist/src/executor/slack-executor.d.ts +24 -0
- package/dist/src/executor/slack-executor.d.ts.map +1 -0
- package/dist/src/executor/slack-executor.js +147 -0
- package/dist/src/executor/slack-executor.js.map +1 -0
- package/dist/src/index.d.ts +25 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +74 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/mcp/auth-verifier.d.ts +23 -0
- package/dist/src/mcp/auth-verifier.d.ts.map +1 -0
- package/dist/src/mcp/auth-verifier.js +162 -0
- package/dist/src/mcp/auth-verifier.js.map +1 -0
- package/dist/src/mcp/bridge.d.ts +132 -0
- package/dist/src/mcp/bridge.d.ts.map +1 -0
- package/dist/src/mcp/bridge.js +734 -0
- package/dist/src/mcp/bridge.js.map +1 -0
- package/dist/src/mcp/http-transport.d.ts +32 -0
- package/dist/src/mcp/http-transport.d.ts.map +1 -0
- package/dist/src/mcp/http-transport.js +538 -0
- package/dist/src/mcp/http-transport.js.map +1 -0
- package/dist/src/mcp/index.d.ts +10 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +17 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/mcp/oauth-pages.d.ts +23 -0
- package/dist/src/mcp/oauth-pages.d.ts.map +1 -0
- package/dist/src/mcp/oauth-pages.js +121 -0
- package/dist/src/mcp/oauth-pages.js.map +1 -0
- package/dist/src/mcp/oauth-postgres-stores.d.ts +55 -0
- package/dist/src/mcp/oauth-postgres-stores.d.ts.map +1 -0
- package/dist/src/mcp/oauth-postgres-stores.js +226 -0
- package/dist/src/mcp/oauth-postgres-stores.js.map +1 -0
- package/dist/src/mcp/oauth-provider.d.ts +95 -0
- package/dist/src/mcp/oauth-provider.d.ts.map +1 -0
- package/dist/src/mcp/oauth-provider.js +360 -0
- package/dist/src/mcp/oauth-provider.js.map +1 -0
- package/dist/src/mcp/oauth-stores.d.ts +62 -0
- package/dist/src/mcp/oauth-stores.d.ts.map +1 -0
- package/dist/src/mcp/oauth-stores.js +154 -0
- package/dist/src/mcp/oauth-stores.js.map +1 -0
- package/dist/src/mcp/server.d.ts +18 -0
- package/dist/src/mcp/server.d.ts.map +1 -0
- package/dist/src/mcp/server.js +51 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/metrics/collector.d.ts +106 -0
- package/dist/src/metrics/collector.d.ts.map +1 -0
- package/dist/src/metrics/collector.js +311 -0
- package/dist/src/metrics/collector.js.map +1 -0
- package/dist/src/metrics/index.d.ts +2 -0
- package/dist/src/metrics/index.d.ts.map +1 -0
- package/dist/src/metrics/index.js +6 -0
- package/dist/src/metrics/index.js.map +1 -0
- package/dist/src/middleware/auth.d.ts +77 -0
- package/dist/src/middleware/auth.d.ts.map +1 -0
- package/dist/src/middleware/auth.js +720 -0
- package/dist/src/middleware/auth.js.map +1 -0
- package/dist/src/middleware/session.d.ts +18 -0
- package/dist/src/middleware/session.d.ts.map +1 -0
- package/dist/src/middleware/session.js +67 -0
- package/dist/src/middleware/session.js.map +1 -0
- package/dist/src/middleware/validate.d.ts +3 -0
- package/dist/src/middleware/validate.d.ts.map +1 -0
- package/dist/src/middleware/validate.js +85 -0
- package/dist/src/middleware/validate.js.map +1 -0
- package/dist/src/policy/engine.d.ts +107 -0
- package/dist/src/policy/engine.d.ts.map +1 -0
- package/dist/src/policy/engine.js +646 -0
- package/dist/src/policy/engine.js.map +1 -0
- package/dist/src/policy/index.d.ts +3 -0
- package/dist/src/policy/index.d.ts.map +1 -0
- package/dist/src/policy/index.js +8 -0
- package/dist/src/policy/index.js.map +1 -0
- package/dist/src/policy/opa-engine.d.ts +176 -0
- package/dist/src/policy/opa-engine.d.ts.map +1 -0
- package/dist/src/policy/opa-engine.js +790 -0
- package/dist/src/policy/opa-engine.js.map +1 -0
- package/dist/src/proxy/forward-proxy.d.ts +30 -0
- package/dist/src/proxy/forward-proxy.d.ts.map +1 -0
- package/dist/src/proxy/forward-proxy.js +580 -0
- package/dist/src/proxy/forward-proxy.js.map +1 -0
- package/dist/src/proxy/index.d.ts +2 -0
- package/dist/src/proxy/index.d.ts.map +1 -0
- package/dist/src/proxy/index.js +8 -0
- package/dist/src/proxy/index.js.map +1 -0
- package/dist/src/ratelimit/limiter.d.ts +45 -0
- package/dist/src/ratelimit/limiter.d.ts.map +1 -0
- package/dist/src/ratelimit/limiter.js +158 -0
- package/dist/src/ratelimit/limiter.js.map +1 -0
- package/dist/src/replay/engine.d.ts +40 -0
- package/dist/src/replay/engine.d.ts.map +1 -0
- package/dist/src/replay/engine.js +106 -0
- package/dist/src/replay/engine.js.map +1 -0
- package/dist/src/replay/index.d.ts +2 -0
- package/dist/src/replay/index.d.ts.map +1 -0
- package/dist/src/replay/index.js +6 -0
- package/dist/src/replay/index.js.map +1 -0
- package/dist/src/saas/index.d.ts +2 -0
- package/dist/src/saas/index.d.ts.map +1 -0
- package/dist/src/saas/index.js +18 -0
- package/dist/src/saas/index.js.map +1 -0
- package/dist/src/saas/routes.d.ts +18 -0
- package/dist/src/saas/routes.d.ts.map +1 -0
- package/dist/src/saas/routes.js +1566 -0
- package/dist/src/saas/routes.js.map +1 -0
- package/dist/src/server/app.d.ts +44 -0
- package/dist/src/server/app.d.ts.map +1 -0
- package/dist/src/server/app.js +854 -0
- package/dist/src/server/app.js.map +1 -0
- package/dist/src/server/errors.d.ts +32 -0
- package/dist/src/server/errors.d.ts.map +1 -0
- package/dist/src/server/errors.js +39 -0
- package/dist/src/server/errors.js.map +1 -0
- package/dist/src/server/gateway.d.ts +165 -0
- package/dist/src/server/gateway.d.ts.map +1 -0
- package/dist/src/server/gateway.js +964 -0
- package/dist/src/server/gateway.js.map +1 -0
- package/dist/src/server/index.d.ts +2 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +295 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/server/logger.d.ts +33 -0
- package/dist/src/server/logger.d.ts.map +1 -0
- package/dist/src/server/logger.js +230 -0
- package/dist/src/server/logger.js.map +1 -0
- package/dist/src/server/stream-proxy.d.ts +32 -0
- package/dist/src/server/stream-proxy.d.ts.map +1 -0
- package/dist/src/server/stream-proxy.js +184 -0
- package/dist/src/server/stream-proxy.js.map +1 -0
- package/dist/src/storage/file-persistence.d.ts +48 -0
- package/dist/src/storage/file-persistence.d.ts.map +1 -0
- package/dist/src/storage/file-persistence.js +280 -0
- package/dist/src/storage/file-persistence.js.map +1 -0
- package/dist/src/storage/index.d.ts +5 -0
- package/dist/src/storage/index.d.ts.map +1 -0
- package/dist/src/storage/index.js +21 -0
- package/dist/src/storage/index.js.map +1 -0
- package/dist/src/storage/interfaces.d.ts +237 -0
- package/dist/src/storage/interfaces.d.ts.map +1 -0
- package/dist/src/storage/interfaces.js +3 -0
- package/dist/src/storage/interfaces.js.map +1 -0
- package/dist/src/storage/memory.d.ts +162 -0
- package/dist/src/storage/memory.d.ts.map +1 -0
- package/dist/src/storage/memory.js +603 -0
- package/dist/src/storage/memory.js.map +1 -0
- package/dist/src/storage/postgres.d.ts +267 -0
- package/dist/src/storage/postgres.d.ts.map +1 -0
- package/dist/src/storage/postgres.js +1555 -0
- package/dist/src/storage/postgres.js.map +1 -0
- package/dist/src/storage/redis.d.ts +202 -0
- package/dist/src/storage/redis.d.ts.map +1 -0
- package/dist/src/storage/redis.js +629 -0
- package/dist/src/storage/redis.js.map +1 -0
- package/dist/src/tracing/index.d.ts +2 -0
- package/dist/src/tracing/index.d.ts.map +1 -0
- package/dist/src/tracing/index.js +6 -0
- package/dist/src/tracing/index.js.map +1 -0
- package/dist/src/tracing/provider.d.ts +43 -0
- package/dist/src/tracing/provider.d.ts.map +1 -0
- package/dist/src/tracing/provider.js +74 -0
- package/dist/src/tracing/provider.js.map +1 -0
- package/dist/src/trust/calculator.d.ts +54 -0
- package/dist/src/trust/calculator.d.ts.map +1 -0
- package/dist/src/trust/calculator.js +102 -0
- package/dist/src/trust/calculator.js.map +1 -0
- package/dist/src/trust/index.d.ts +2 -0
- package/dist/src/trust/index.d.ts.map +1 -0
- package/dist/src/trust/index.js +7 -0
- package/dist/src/trust/index.js.map +1 -0
- package/dist/src/types/budget.d.ts +30 -0
- package/dist/src/types/budget.d.ts.map +1 -0
- package/dist/src/types/budget.js +3 -0
- package/dist/src/types/budget.js.map +1 -0
- package/dist/src/types/config.d.ts +176 -0
- package/dist/src/types/config.d.ts.map +1 -0
- package/dist/src/types/config.js +3 -0
- package/dist/src/types/config.js.map +1 -0
- package/dist/src/types/events.d.ts +24 -0
- package/dist/src/types/events.d.ts.map +1 -0
- package/dist/src/types/events.js +3 -0
- package/dist/src/types/events.js.map +1 -0
- package/dist/src/types/index.d.ts +8 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +24 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/policy.d.ts +60 -0
- package/dist/src/types/policy.d.ts.map +1 -0
- package/dist/src/types/policy.js +3 -0
- package/dist/src/types/policy.js.map +1 -0
- package/dist/src/types/stripe-config.d.ts +12 -0
- package/dist/src/types/stripe-config.d.ts.map +1 -0
- package/dist/src/types/stripe-config.js +3 -0
- package/dist/src/types/stripe-config.js.map +1 -0
- package/dist/src/types/subscription.d.ts +24 -0
- package/dist/src/types/subscription.d.ts.map +1 -0
- package/dist/src/types/subscription.js +38 -0
- package/dist/src/types/subscription.js.map +1 -0
- package/dist/src/types/tool-call.d.ts +42 -0
- package/dist/src/types/tool-call.d.ts.map +1 -0
- package/dist/src/types/tool-call.js +3 -0
- package/dist/src/types/tool-call.js.map +1 -0
- package/dist/src/types/tool-result.d.ts +58 -0
- package/dist/src/types/tool-result.d.ts.map +1 -0
- package/dist/src/types/tool-result.js +3 -0
- package/dist/src/types/tool-result.js.map +1 -0
- package/dist/src/types/user.d.ts +101 -0
- package/dist/src/types/user.d.ts.map +1 -0
- package/dist/src/types/user.js +6 -0
- package/dist/src/types/user.js.map +1 -0
- package/dist/tests/integration/api.test.d.ts +2 -0
- package/dist/tests/integration/api.test.d.ts.map +1 -0
- package/dist/tests/integration/api.test.js +1199 -0
- package/dist/tests/integration/api.test.js.map +1 -0
- package/dist/tests/integration/proxy.test.d.ts +2 -0
- package/dist/tests/integration/proxy.test.d.ts.map +1 -0
- package/dist/tests/integration/proxy.test.js +251 -0
- package/dist/tests/integration/proxy.test.js.map +1 -0
- package/dist/tests/integration/storage.test.d.ts +16 -0
- package/dist/tests/integration/storage.test.d.ts.map +1 -0
- package/dist/tests/integration/storage.test.js +826 -0
- package/dist/tests/integration/storage.test.js.map +1 -0
- package/dist/tests/unit/admin.test.d.ts +2 -0
- package/dist/tests/unit/admin.test.d.ts.map +1 -0
- package/dist/tests/unit/admin.test.js +698 -0
- package/dist/tests/unit/admin.test.js.map +1 -0
- package/dist/tests/unit/anomaly-detector.test.d.ts +2 -0
- package/dist/tests/unit/anomaly-detector.test.d.ts.map +1 -0
- package/dist/tests/unit/anomaly-detector.test.js +903 -0
- package/dist/tests/unit/anomaly-detector.test.js.map +1 -0
- package/dist/tests/unit/approval-manager.test.d.ts +2 -0
- package/dist/tests/unit/approval-manager.test.d.ts.map +1 -0
- package/dist/tests/unit/approval-manager.test.js +528 -0
- package/dist/tests/unit/approval-manager.test.js.map +1 -0
- package/dist/tests/unit/approval-webhook.test.d.ts +2 -0
- package/dist/tests/unit/approval-webhook.test.d.ts.map +1 -0
- package/dist/tests/unit/approval-webhook.test.js +355 -0
- package/dist/tests/unit/approval-webhook.test.js.map +1 -0
- package/dist/tests/unit/audit-logger.test.d.ts +2 -0
- package/dist/tests/unit/audit-logger.test.d.ts.map +1 -0
- package/dist/tests/unit/audit-logger.test.js +635 -0
- package/dist/tests/unit/audit-logger.test.js.map +1 -0
- package/dist/tests/unit/auth-routes.test.d.ts +2 -0
- package/dist/tests/unit/auth-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-routes.test.js +281 -0
- package/dist/tests/unit/auth-routes.test.js.map +1 -0
- package/dist/tests/unit/auth.test.d.ts +2 -0
- package/dist/tests/unit/auth.test.d.ts.map +1 -0
- package/dist/tests/unit/auth.test.js +1382 -0
- package/dist/tests/unit/auth.test.js.map +1 -0
- package/dist/tests/unit/billing.test.d.ts +2 -0
- package/dist/tests/unit/billing.test.d.ts.map +1 -0
- package/dist/tests/unit/billing.test.js +579 -0
- package/dist/tests/unit/billing.test.js.map +1 -0
- package/dist/tests/unit/budget-manager.test.d.ts +2 -0
- package/dist/tests/unit/budget-manager.test.d.ts.map +1 -0
- package/dist/tests/unit/budget-manager.test.js +778 -0
- package/dist/tests/unit/budget-manager.test.js.map +1 -0
- package/dist/tests/unit/budget-race.test.d.ts +2 -0
- package/dist/tests/unit/budget-race.test.d.ts.map +1 -0
- package/dist/tests/unit/budget-race.test.js +58 -0
- package/dist/tests/unit/budget-race.test.js.map +1 -0
- package/dist/tests/unit/cli.test.d.ts +2 -0
- package/dist/tests/unit/cli.test.d.ts.map +1 -0
- package/dist/tests/unit/cli.test.js +93 -0
- package/dist/tests/unit/cli.test.js.map +1 -0
- package/dist/tests/unit/concurrency.test.d.ts +2 -0
- package/dist/tests/unit/concurrency.test.d.ts.map +1 -0
- package/dist/tests/unit/concurrency.test.js +1270 -0
- package/dist/tests/unit/concurrency.test.js.map +1 -0
- package/dist/tests/unit/config-validate.test.d.ts +2 -0
- package/dist/tests/unit/config-validate.test.d.ts.map +1 -0
- package/dist/tests/unit/config-validate.test.js +230 -0
- package/dist/tests/unit/config-validate.test.js.map +1 -0
- package/dist/tests/unit/defaults.test.d.ts +2 -0
- package/dist/tests/unit/defaults.test.d.ts.map +1 -0
- package/dist/tests/unit/defaults.test.js +364 -0
- package/dist/tests/unit/defaults.test.js.map +1 -0
- package/dist/tests/unit/dlp-backends.test.d.ts +2 -0
- package/dist/tests/unit/dlp-backends.test.d.ts.map +1 -0
- package/dist/tests/unit/dlp-backends.test.js +563 -0
- package/dist/tests/unit/dlp-backends.test.js.map +1 -0
- package/dist/tests/unit/dlp-scanner.test.d.ts +2 -0
- package/dist/tests/unit/dlp-scanner.test.d.ts.map +1 -0
- package/dist/tests/unit/dlp-scanner.test.js +739 -0
- package/dist/tests/unit/dlp-scanner.test.js.map +1 -0
- package/dist/tests/unit/error-responses.test.d.ts +2 -0
- package/dist/tests/unit/error-responses.test.d.ts.map +1 -0
- package/dist/tests/unit/error-responses.test.js +101 -0
- package/dist/tests/unit/error-responses.test.js.map +1 -0
- package/dist/tests/unit/executor-registry.test.d.ts +2 -0
- package/dist/tests/unit/executor-registry.test.d.ts.map +1 -0
- package/dist/tests/unit/executor-registry.test.js +390 -0
- package/dist/tests/unit/executor-registry.test.js.map +1 -0
- package/dist/tests/unit/forward-proxy.test.d.ts +2 -0
- package/dist/tests/unit/forward-proxy.test.d.ts.map +1 -0
- package/dist/tests/unit/forward-proxy.test.js +621 -0
- package/dist/tests/unit/forward-proxy.test.js.map +1 -0
- package/dist/tests/unit/gateway-features.test.d.ts +2 -0
- package/dist/tests/unit/gateway-features.test.d.ts.map +1 -0
- package/dist/tests/unit/gateway-features.test.js +753 -0
- package/dist/tests/unit/gateway-features.test.js.map +1 -0
- package/dist/tests/unit/http-executor.test.d.ts +2 -0
- package/dist/tests/unit/http-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/http-executor.test.js +310 -0
- package/dist/tests/unit/http-executor.test.js.map +1 -0
- package/dist/tests/unit/mcp-bridge.test.d.ts +2 -0
- package/dist/tests/unit/mcp-bridge.test.d.ts.map +1 -0
- package/dist/tests/unit/mcp-bridge.test.js +1136 -0
- package/dist/tests/unit/mcp-bridge.test.js.map +1 -0
- package/dist/tests/unit/mcp-http-transport.test.d.ts +2 -0
- package/dist/tests/unit/mcp-http-transport.test.d.ts.map +1 -0
- package/dist/tests/unit/mcp-http-transport.test.js +899 -0
- package/dist/tests/unit/mcp-http-transport.test.js.map +1 -0
- package/dist/tests/unit/mcp-oauth.test.d.ts +2 -0
- package/dist/tests/unit/mcp-oauth.test.d.ts.map +1 -0
- package/dist/tests/unit/mcp-oauth.test.js +759 -0
- package/dist/tests/unit/mcp-oauth.test.js.map +1 -0
- package/dist/tests/unit/mcp-server.test.d.ts +15 -0
- package/dist/tests/unit/mcp-server.test.d.ts.map +1 -0
- package/dist/tests/unit/mcp-server.test.js +158 -0
- package/dist/tests/unit/mcp-server.test.js.map +1 -0
- package/dist/tests/unit/metrics.test.d.ts +2 -0
- package/dist/tests/unit/metrics.test.d.ts.map +1 -0
- package/dist/tests/unit/metrics.test.js +208 -0
- package/dist/tests/unit/metrics.test.js.map +1 -0
- package/dist/tests/unit/oauth.test.d.ts +2 -0
- package/dist/tests/unit/oauth.test.d.ts.map +1 -0
- package/dist/tests/unit/oauth.test.js +281 -0
- package/dist/tests/unit/oauth.test.js.map +1 -0
- package/dist/tests/unit/opa-circuit-breaker.test.d.ts +2 -0
- package/dist/tests/unit/opa-circuit-breaker.test.d.ts.map +1 -0
- package/dist/tests/unit/opa-circuit-breaker.test.js +297 -0
- package/dist/tests/unit/opa-circuit-breaker.test.js.map +1 -0
- package/dist/tests/unit/opa-engine.test.d.ts +2 -0
- package/dist/tests/unit/opa-engine.test.d.ts.map +1 -0
- package/dist/tests/unit/opa-engine.test.js +1813 -0
- package/dist/tests/unit/opa-engine.test.js.map +1 -0
- package/dist/tests/unit/pipeline-timing.test.d.ts +2 -0
- package/dist/tests/unit/pipeline-timing.test.d.ts.map +1 -0
- package/dist/tests/unit/pipeline-timing.test.js +528 -0
- package/dist/tests/unit/pipeline-timing.test.js.map +1 -0
- package/dist/tests/unit/policy-engine.test.d.ts +2 -0
- package/dist/tests/unit/policy-engine.test.d.ts.map +1 -0
- package/dist/tests/unit/policy-engine.test.js +1345 -0
- package/dist/tests/unit/policy-engine.test.js.map +1 -0
- package/dist/tests/unit/policy-store.test.d.ts +2 -0
- package/dist/tests/unit/policy-store.test.d.ts.map +1 -0
- package/dist/tests/unit/policy-store.test.js +60 -0
- package/dist/tests/unit/policy-store.test.js.map +1 -0
- package/dist/tests/unit/postgres-storage.test.d.ts +2 -0
- package/dist/tests/unit/postgres-storage.test.d.ts.map +1 -0
- package/dist/tests/unit/postgres-storage.test.js +614 -0
- package/dist/tests/unit/postgres-storage.test.js.map +1 -0
- package/dist/tests/unit/prompt-injection-backend.test.d.ts +2 -0
- package/dist/tests/unit/prompt-injection-backend.test.d.ts.map +1 -0
- package/dist/tests/unit/prompt-injection-backend.test.js +621 -0
- package/dist/tests/unit/prompt-injection-backend.test.js.map +1 -0
- package/dist/tests/unit/proxy-hardening.test.d.ts +2 -0
- package/dist/tests/unit/proxy-hardening.test.d.ts.map +1 -0
- package/dist/tests/unit/proxy-hardening.test.js +166 -0
- package/dist/tests/unit/proxy-hardening.test.js.map +1 -0
- package/dist/tests/unit/rate-limiter.test.d.ts +2 -0
- package/dist/tests/unit/rate-limiter.test.d.ts.map +1 -0
- package/dist/tests/unit/rate-limiter.test.js +443 -0
- package/dist/tests/unit/rate-limiter.test.js.map +1 -0
- package/dist/tests/unit/redis-storage.test.d.ts +2 -0
- package/dist/tests/unit/redis-storage.test.d.ts.map +1 -0
- package/dist/tests/unit/redis-storage.test.js +766 -0
- package/dist/tests/unit/redis-storage.test.js.map +1 -0
- package/dist/tests/unit/replay-engine.test.d.ts +2 -0
- package/dist/tests/unit/replay-engine.test.d.ts.map +1 -0
- package/dist/tests/unit/replay-engine.test.js +371 -0
- package/dist/tests/unit/replay-engine.test.js.map +1 -0
- package/dist/tests/unit/saas-routes.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes.test.js +1399 -0
- package/dist/tests/unit/saas-routes.test.js.map +1 -0
- package/dist/tests/unit/session.test.d.ts +2 -0
- package/dist/tests/unit/session.test.d.ts.map +1 -0
- package/dist/tests/unit/session.test.js +532 -0
- package/dist/tests/unit/session.test.js.map +1 -0
- package/dist/tests/unit/slack-executor.test.d.ts +2 -0
- package/dist/tests/unit/slack-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/slack-executor.test.js +209 -0
- package/dist/tests/unit/slack-executor.test.js.map +1 -0
- package/dist/tests/unit/storage-hardening.test.d.ts +2 -0
- package/dist/tests/unit/storage-hardening.test.d.ts.map +1 -0
- package/dist/tests/unit/storage-hardening.test.js +165 -0
- package/dist/tests/unit/storage-hardening.test.js.map +1 -0
- package/dist/tests/unit/storage.test.d.ts +2 -0
- package/dist/tests/unit/storage.test.d.ts.map +1 -0
- package/dist/tests/unit/storage.test.js +698 -0
- package/dist/tests/unit/storage.test.js.map +1 -0
- package/dist/tests/unit/text-normalizer.test.d.ts +2 -0
- package/dist/tests/unit/text-normalizer.test.d.ts.map +1 -0
- package/dist/tests/unit/text-normalizer.test.js +229 -0
- package/dist/tests/unit/text-normalizer.test.js.map +1 -0
- package/dist/tests/unit/tracing.test.d.ts +2 -0
- package/dist/tests/unit/tracing.test.d.ts.map +1 -0
- package/dist/tests/unit/tracing.test.js +611 -0
- package/dist/tests/unit/tracing.test.js.map +1 -0
- package/dist/tests/unit/trust-calculator.test.d.ts +2 -0
- package/dist/tests/unit/trust-calculator.test.d.ts.map +1 -0
- package/dist/tests/unit/trust-calculator.test.js +497 -0
- package/dist/tests/unit/trust-calculator.test.js.map +1 -0
- package/dist/tests/unit/ts-sdk.test.d.ts +2 -0
- package/dist/tests/unit/ts-sdk.test.d.ts.map +1 -0
- package/dist/tests/unit/ts-sdk.test.js +421 -0
- package/dist/tests/unit/ts-sdk.test.js.map +1 -0
- package/dist/tests/unit/usage-extractor-llm.test.d.ts +2 -0
- package/dist/tests/unit/usage-extractor-llm.test.d.ts.map +1 -0
- package/dist/tests/unit/usage-extractor-llm.test.js +139 -0
- package/dist/tests/unit/usage-extractor-llm.test.js.map +1 -0
- package/dist/tests/unit/usage-extractor.test.d.ts +2 -0
- package/dist/tests/unit/usage-extractor.test.d.ts.map +1 -0
- package/dist/tests/unit/usage-extractor.test.js +271 -0
- package/dist/tests/unit/usage-extractor.test.js.map +1 -0
- package/dist/tests/unit/user-stores.test.d.ts +2 -0
- package/dist/tests/unit/user-stores.test.d.ts.map +1 -0
- package/dist/tests/unit/user-stores.test.js +687 -0
- package/dist/tests/unit/user-stores.test.js.map +1 -0
- package/dist/tests/unit/validate.test.d.ts +2 -0
- package/dist/tests/unit/validate.test.d.ts.map +1 -0
- package/dist/tests/unit/validate.test.js +545 -0
- package/dist/tests/unit/validate.test.js.map +1 -0
- package/package.json +86 -0
- package/policy-packs/README.md +42 -0
- package/policy-packs/default.yaml +46 -0
- package/policy-packs/dev_fast.yaml +54 -0
- package/policy-packs/prod_strict.yaml +83 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { ToolCall } from '../types/tool-call';
|
|
2
|
+
import { ApprovalConfig } from '../types/config';
|
|
3
|
+
import { ApprovalStore } from '../storage/interfaces';
|
|
4
|
+
export interface PendingApproval {
|
|
5
|
+
approval_id: string;
|
|
6
|
+
tool_call_id: string;
|
|
7
|
+
task_id: string;
|
|
8
|
+
workspace_id: string;
|
|
9
|
+
actor_id: string;
|
|
10
|
+
requesting_api_key_id?: string;
|
|
11
|
+
tool_name: string;
|
|
12
|
+
tool_capability: string;
|
|
13
|
+
args_summary: string;
|
|
14
|
+
scope: string;
|
|
15
|
+
reason: string;
|
|
16
|
+
token_hash: string;
|
|
17
|
+
status: 'pending' | 'approved' | 'denied' | 'expired';
|
|
18
|
+
created_at: string;
|
|
19
|
+
expires_at: string;
|
|
20
|
+
resolved_at?: string;
|
|
21
|
+
resolved_by?: string;
|
|
22
|
+
denial_reason?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CreateApprovalResult {
|
|
25
|
+
approval: PendingApproval;
|
|
26
|
+
token: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ApprovalResult {
|
|
29
|
+
approved: boolean;
|
|
30
|
+
approval_id: string;
|
|
31
|
+
resolved_by?: string;
|
|
32
|
+
denial_reason?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class ApprovalManager {
|
|
35
|
+
private config;
|
|
36
|
+
private store;
|
|
37
|
+
private webhook;
|
|
38
|
+
private requireDifferentIdentity;
|
|
39
|
+
/** Track approval IDs currently being processed to prevent concurrent resolution */
|
|
40
|
+
private inFlightApprovals;
|
|
41
|
+
/**
|
|
42
|
+
* Track used approval tokens (token hash -> usage timestamp) to prevent replay attacks.
|
|
43
|
+
* This is defense-in-depth; the primary defense against replay is the approval status
|
|
44
|
+
* check in the store (which persists across restarts).
|
|
45
|
+
*/
|
|
46
|
+
private usedTokens;
|
|
47
|
+
constructor(config: ApprovalConfig, store?: ApprovalStore, options?: {
|
|
48
|
+
require_different_identity?: boolean;
|
|
49
|
+
});
|
|
50
|
+
/**
|
|
51
|
+
* Create a pending approval for a tool call that received a REQUIRE_APPROVAL
|
|
52
|
+
* decision from the policy engine. Generates a signed JWT token that must be
|
|
53
|
+
* presented when approving or denying the request.
|
|
54
|
+
*/
|
|
55
|
+
createApproval(toolCall: ToolCall, scope: string, reason: string, ttlSeconds?: number, requestingApiKeyId?: string): CreateApprovalResult;
|
|
56
|
+
/**
|
|
57
|
+
* Approve a pending request by presenting the signed JWT token.
|
|
58
|
+
*
|
|
59
|
+
* Verifies the token signature and expiry, locates the corresponding
|
|
60
|
+
* approval record, confirms it is still in 'pending' status, and
|
|
61
|
+
* transitions it to 'approved'.
|
|
62
|
+
*
|
|
63
|
+
* Throws an Error for invalid tokens, expired approvals, or approvals
|
|
64
|
+
* that have already been resolved.
|
|
65
|
+
*/
|
|
66
|
+
approve(token: string, approverId: string, approverApiKeyId?: string): Promise<ApprovalResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Deny a pending request by presenting the signed JWT token along with a
|
|
69
|
+
* reason for denial.
|
|
70
|
+
*
|
|
71
|
+
* Verifies the token signature and expiry, locates the corresponding
|
|
72
|
+
* approval record, confirms it is still in 'pending' status, and
|
|
73
|
+
* transitions it to 'denied'.
|
|
74
|
+
*
|
|
75
|
+
* Throws an Error for invalid tokens, expired approvals, or approvals
|
|
76
|
+
* that have already been resolved.
|
|
77
|
+
*/
|
|
78
|
+
deny(token: string, approverId: string, reason: string, _approverApiKeyId?: string): Promise<ApprovalResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Resolve an approval by its ID (for admin panel use where the raw token is
|
|
81
|
+
* not available). Validates the approval is still pending and not expired.
|
|
82
|
+
*/
|
|
83
|
+
resolveById(approvalId: string, approverId: string, approved: boolean, reason?: string, approverApiKeyId?: string): Promise<ApprovalResult>;
|
|
84
|
+
/**
|
|
85
|
+
* Check if an approval exists and return its current status.
|
|
86
|
+
* Automatically marks the approval as 'expired' if it has passed its TTL.
|
|
87
|
+
*/
|
|
88
|
+
getApproval(approvalId: string): PendingApproval | null;
|
|
89
|
+
/**
|
|
90
|
+
* Look up an approval by the original tool_call_id. This is useful when an
|
|
91
|
+
* agent retries a tool call and needs to find the approval that was created
|
|
92
|
+
* for the original attempt.
|
|
93
|
+
*
|
|
94
|
+
* Automatically marks the approval as 'expired' if it has passed its TTL.
|
|
95
|
+
*/
|
|
96
|
+
getApprovalByToolCallId(toolCallId: string): PendingApproval | null;
|
|
97
|
+
/**
|
|
98
|
+
* Find an existing approved approval for the same task+actor+tool combination.
|
|
99
|
+
* Used to bypass re-approval when an agent retries after a previous approval.
|
|
100
|
+
*/
|
|
101
|
+
findApprovedForTask(taskId: string, actorId: string, toolName: string, capability: string): PendingApproval | null;
|
|
102
|
+
/**
|
|
103
|
+
* Get all currently pending approvals, optionally filtered by workspace.
|
|
104
|
+
* Expires any approvals that are past their TTL before returning results.
|
|
105
|
+
*/
|
|
106
|
+
getPendingApprovals(workspaceId?: string): PendingApproval[];
|
|
107
|
+
/**
|
|
108
|
+
* Verify and decode a JWT approval token.
|
|
109
|
+
* Returns the decoded payload on success, or an error message on failure.
|
|
110
|
+
*/
|
|
111
|
+
private verifyToken;
|
|
112
|
+
/**
|
|
113
|
+
* Common logic shared by approve() and deny(): verify the token, look up
|
|
114
|
+
* the approval record, and validate that it can still be resolved.
|
|
115
|
+
*
|
|
116
|
+
* Returns the mutable PendingApproval record so the caller can update its
|
|
117
|
+
* status fields directly.
|
|
118
|
+
*
|
|
119
|
+
* Throws an Error if:
|
|
120
|
+
* - The token is invalid or has an expired signature
|
|
121
|
+
* - No approval record is found for the token
|
|
122
|
+
* - The approval has already been resolved (approved/denied)
|
|
123
|
+
* - The approval has expired (past TTL)
|
|
124
|
+
*/
|
|
125
|
+
private resolveTokenToApproval;
|
|
126
|
+
/**
|
|
127
|
+
* Create a sanitized summary of the tool call arguments that is safe to
|
|
128
|
+
* display to an approver. Strips query parameters (may contain tokens or
|
|
129
|
+
* secrets), omits headers entirely, and only indicates whether a body is
|
|
130
|
+
* present without revealing its contents.
|
|
131
|
+
*/
|
|
132
|
+
private sanitizeArgs;
|
|
133
|
+
/**
|
|
134
|
+
* Scan all pending approvals and mark any that have passed their TTL as
|
|
135
|
+
* 'expired'. Returns the number of approvals that were expired.
|
|
136
|
+
*/
|
|
137
|
+
cleanup(): number;
|
|
138
|
+
/**
|
|
139
|
+
* Wait for all pending store writes to complete.
|
|
140
|
+
*/
|
|
141
|
+
flush(): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Clear all approval state. Intended for use in tests only.
|
|
144
|
+
*/
|
|
145
|
+
clear(): void;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/approval/manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAWtD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,eAAe,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,wBAAwB,CAAU;IAC1C,oFAAoF;IACpF,OAAO,CAAC,iBAAiB,CAAqB;IAC9C;;;;OAIG;IACH,OAAO,CAAC,UAAU,CAA6B;gBAEnC,MAAM,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE;QAAE,0BAA0B,CAAC,EAAE,OAAO,CAAA;KAAE;IAS7G;;;;OAIG;IACH,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,MAAM,GAAG,oBAAoB;IA0DzI;;;;;;;;;OASG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAgFpG;;;;;;;;;;OAUG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA+DlH;;;OAGG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IA2EjJ;;;OAGG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAYvD;;;;;;OAMG;IACH,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAWnE;;;OAGG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI;IAOlH;;;OAGG;IACH,mBAAmB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE;IAkB5D;;;OAGG;IACH,OAAO,CAAC,WAAW;IAUnB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,sBAAsB;IAiC9B;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAsBpB;;;OAGG;IACH,OAAO,IAAI,MAAM;IAejB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ApprovalManager = void 0;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const jwt = __importStar(require("jsonwebtoken"));
|
|
39
|
+
const crypto_1 = require("crypto");
|
|
40
|
+
const webhook_1 = require("./webhook");
|
|
41
|
+
const memory_1 = require("../storage/memory");
|
|
42
|
+
/** SHA-256 hash a string and return the hex digest */
|
|
43
|
+
function hashToken(token) {
|
|
44
|
+
return crypto.createHash('sha256').update(token).digest('hex');
|
|
45
|
+
}
|
|
46
|
+
/** Maximum number of used token hashes to track (prevents unbounded memory growth) */
|
|
47
|
+
const MAX_USED_TOKENS = 100000;
|
|
48
|
+
class ApprovalManager {
|
|
49
|
+
constructor(config, store, options) {
|
|
50
|
+
/** Track approval IDs currently being processed to prevent concurrent resolution */
|
|
51
|
+
this.inFlightApprovals = new Set();
|
|
52
|
+
/**
|
|
53
|
+
* Track used approval tokens (token hash -> usage timestamp) to prevent replay attacks.
|
|
54
|
+
* This is defense-in-depth; the primary defense against replay is the approval status
|
|
55
|
+
* check in the store (which persists across restarts).
|
|
56
|
+
*/
|
|
57
|
+
this.usedTokens = new Map();
|
|
58
|
+
this.config = config;
|
|
59
|
+
this.store = store ?? new memory_1.InMemoryApprovalStore();
|
|
60
|
+
this.webhook = config.webhook_url
|
|
61
|
+
? new webhook_1.ApprovalWebhook(config.webhook_url, config.webhook_headers)
|
|
62
|
+
: null;
|
|
63
|
+
this.requireDifferentIdentity = options?.require_different_identity ?? true;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a pending approval for a tool call that received a REQUIRE_APPROVAL
|
|
67
|
+
* decision from the policy engine. Generates a signed JWT token that must be
|
|
68
|
+
* presented when approving or denying the request.
|
|
69
|
+
*/
|
|
70
|
+
createApproval(toolCall, scope, reason, ttlSeconds, requestingApiKeyId) {
|
|
71
|
+
const approvalId = (0, crypto_1.randomUUID)();
|
|
72
|
+
const ttl = ttlSeconds || this.config.default_ttl_seconds;
|
|
73
|
+
const now = new Date();
|
|
74
|
+
const expiresAt = new Date(now.getTime() + ttl * 1000);
|
|
75
|
+
// Create JWT token containing approval metadata
|
|
76
|
+
const token = jwt.sign({
|
|
77
|
+
approval_id: approvalId,
|
|
78
|
+
tool_call_id: toolCall.tool_call_id,
|
|
79
|
+
task_id: toolCall.task_id,
|
|
80
|
+
workspace_id: toolCall.workspace_id,
|
|
81
|
+
scope,
|
|
82
|
+
}, this.config.token_secret, { expiresIn: ttl });
|
|
83
|
+
// Hash the token before storing (never persist the raw JWT)
|
|
84
|
+
const tokenHash = hashToken(token);
|
|
85
|
+
// Create sanitized args summary (strip sensitive fields)
|
|
86
|
+
const argsSummary = this.sanitizeArgs(toolCall);
|
|
87
|
+
// Hash the requesting API key ID to prevent storing raw keys
|
|
88
|
+
const requestingKeyHash = requestingApiKeyId
|
|
89
|
+
? hashToken(requestingApiKeyId)
|
|
90
|
+
: undefined;
|
|
91
|
+
const approval = {
|
|
92
|
+
approval_id: approvalId,
|
|
93
|
+
tool_call_id: toolCall.tool_call_id,
|
|
94
|
+
task_id: toolCall.task_id,
|
|
95
|
+
workspace_id: toolCall.workspace_id,
|
|
96
|
+
actor_id: toolCall.actor.id,
|
|
97
|
+
requesting_api_key_id: requestingKeyHash,
|
|
98
|
+
tool_name: toolCall.tool.name,
|
|
99
|
+
tool_capability: toolCall.tool.capability,
|
|
100
|
+
args_summary: argsSummary,
|
|
101
|
+
scope,
|
|
102
|
+
reason,
|
|
103
|
+
token_hash: tokenHash,
|
|
104
|
+
status: 'pending',
|
|
105
|
+
created_at: now.toISOString(),
|
|
106
|
+
expires_at: expiresAt.toISOString(),
|
|
107
|
+
};
|
|
108
|
+
this.store.save(approvalId, approval);
|
|
109
|
+
this.store.indexToken(tokenHash, approvalId);
|
|
110
|
+
if (this.webhook) {
|
|
111
|
+
this.webhook.notify('approval_requested', approval);
|
|
112
|
+
}
|
|
113
|
+
return { approval, token };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Approve a pending request by presenting the signed JWT token.
|
|
117
|
+
*
|
|
118
|
+
* Verifies the token signature and expiry, locates the corresponding
|
|
119
|
+
* approval record, confirms it is still in 'pending' status, and
|
|
120
|
+
* transitions it to 'approved'.
|
|
121
|
+
*
|
|
122
|
+
* Throws an Error for invalid tokens, expired approvals, or approvals
|
|
123
|
+
* that have already been resolved.
|
|
124
|
+
*/
|
|
125
|
+
async approve(token, approverId, approverApiKeyId) {
|
|
126
|
+
const tokenHash = hashToken(token);
|
|
127
|
+
// Token replay prevention: check if this token was already used
|
|
128
|
+
if (this.usedTokens.has(tokenHash)) {
|
|
129
|
+
throw new Error('Approval token has already been used');
|
|
130
|
+
}
|
|
131
|
+
const approval = this.resolveTokenToApproval(token);
|
|
132
|
+
// In-process lock: prevent concurrent resolution of the same approval
|
|
133
|
+
if (this.inFlightApprovals.has(approval.approval_id)) {
|
|
134
|
+
throw new Error(`Approval "${approval.approval_id}" is already being processed by another request`);
|
|
135
|
+
}
|
|
136
|
+
this.inFlightApprovals.add(approval.approval_id);
|
|
137
|
+
try {
|
|
138
|
+
// Prevent self-approval via API key comparison (cryptographic identity)
|
|
139
|
+
if (approval.requesting_api_key_id && approverApiKeyId) {
|
|
140
|
+
const approverKeyHash = hashToken(approverApiKeyId);
|
|
141
|
+
if (approverKeyHash === approval.requesting_api_key_id) {
|
|
142
|
+
console.warn(`[ApprovalManager] Self-approval attempt blocked: approver=${approverId}, approval=${approval.approval_id}, workspace=${approval.workspace_id}`);
|
|
143
|
+
throw new Error('Self-approval is not allowed. A different API key must approve this request.');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (this.requireDifferentIdentity && (!approval.requesting_api_key_id || !approverApiKeyId)) {
|
|
147
|
+
// Without cryptographic identity on both sides, deny self-approval entirely
|
|
148
|
+
throw new Error('Self-approval not allowed without cryptographic identity (API key required)');
|
|
149
|
+
}
|
|
150
|
+
// Actor-level self-approval check: prevent same actor from approving their own request
|
|
151
|
+
if (approval.actor_id && approverId && approval.actor_id === approverId) {
|
|
152
|
+
console.warn(`[ApprovalManager] Self-approval attempt blocked (actor match): actor=${approverId}, approval=${approval.approval_id}, workspace=${approval.workspace_id}`);
|
|
153
|
+
throw new Error('Self-approval is not allowed. A different user must approve this request.');
|
|
154
|
+
}
|
|
155
|
+
// Build updated approval object (do NOT mutate the original before CAS)
|
|
156
|
+
const updated = {
|
|
157
|
+
...approval,
|
|
158
|
+
status: 'approved',
|
|
159
|
+
resolved_at: new Date().toISOString(),
|
|
160
|
+
resolved_by: approverId,
|
|
161
|
+
};
|
|
162
|
+
// Atomic CAS: use compareAndSetStatus if available to prevent race conditions
|
|
163
|
+
// where two concurrent approve() calls both resolve the same pending approval
|
|
164
|
+
if (this.store.compareAndSetStatus) {
|
|
165
|
+
const swapped = await this.store.compareAndSetStatus(approval.approval_id, 'pending', updated);
|
|
166
|
+
if (!swapped) {
|
|
167
|
+
throw new Error(`Approval "${approval.approval_id}" was already resolved by another request`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.store.save(approval.approval_id, updated);
|
|
172
|
+
}
|
|
173
|
+
// Mark token as used to prevent replay
|
|
174
|
+
this.usedTokens.set(tokenHash, Date.now());
|
|
175
|
+
if (this.usedTokens.size > MAX_USED_TOKENS) {
|
|
176
|
+
const evictCount = Math.floor(MAX_USED_TOKENS * 0.1);
|
|
177
|
+
let deleted = 0;
|
|
178
|
+
for (const key of this.usedTokens.keys()) {
|
|
179
|
+
if (deleted >= evictCount)
|
|
180
|
+
break;
|
|
181
|
+
this.usedTokens.delete(key);
|
|
182
|
+
deleted++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (this.webhook) {
|
|
186
|
+
this.webhook.notify('approval_approved', updated);
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
approved: true,
|
|
190
|
+
approval_id: approval.approval_id,
|
|
191
|
+
resolved_by: approverId,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
this.inFlightApprovals.delete(approval.approval_id);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Deny a pending request by presenting the signed JWT token along with a
|
|
200
|
+
* reason for denial.
|
|
201
|
+
*
|
|
202
|
+
* Verifies the token signature and expiry, locates the corresponding
|
|
203
|
+
* approval record, confirms it is still in 'pending' status, and
|
|
204
|
+
* transitions it to 'denied'.
|
|
205
|
+
*
|
|
206
|
+
* Throws an Error for invalid tokens, expired approvals, or approvals
|
|
207
|
+
* that have already been resolved.
|
|
208
|
+
*/
|
|
209
|
+
async deny(token, approverId, reason, _approverApiKeyId) {
|
|
210
|
+
const tokenHash = hashToken(token);
|
|
211
|
+
// Token replay prevention: check if this token was already used
|
|
212
|
+
if (this.usedTokens.has(tokenHash)) {
|
|
213
|
+
throw new Error('Approval token has already been used');
|
|
214
|
+
}
|
|
215
|
+
const approval = this.resolveTokenToApproval(token);
|
|
216
|
+
// In-process lock: prevent concurrent resolution of the same approval
|
|
217
|
+
if (this.inFlightApprovals.has(approval.approval_id)) {
|
|
218
|
+
throw new Error(`Approval "${approval.approval_id}" is already being processed by another request`);
|
|
219
|
+
}
|
|
220
|
+
this.inFlightApprovals.add(approval.approval_id);
|
|
221
|
+
try {
|
|
222
|
+
// Build updated approval object (do NOT mutate the original before CAS)
|
|
223
|
+
const updated = {
|
|
224
|
+
...approval,
|
|
225
|
+
status: 'denied',
|
|
226
|
+
resolved_at: new Date().toISOString(),
|
|
227
|
+
resolved_by: approverId,
|
|
228
|
+
denial_reason: reason,
|
|
229
|
+
};
|
|
230
|
+
// Atomic CAS: use compareAndSetStatus if available to prevent race conditions
|
|
231
|
+
if (this.store.compareAndSetStatus) {
|
|
232
|
+
const swapped = await this.store.compareAndSetStatus(approval.approval_id, 'pending', updated);
|
|
233
|
+
if (!swapped) {
|
|
234
|
+
throw new Error(`Approval "${approval.approval_id}" was already resolved by another request`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
this.store.save(approval.approval_id, updated);
|
|
239
|
+
}
|
|
240
|
+
// Mark token as used to prevent replay
|
|
241
|
+
this.usedTokens.set(tokenHash, Date.now());
|
|
242
|
+
if (this.usedTokens.size > MAX_USED_TOKENS) {
|
|
243
|
+
const evictCount = Math.floor(MAX_USED_TOKENS * 0.1);
|
|
244
|
+
let deleted = 0;
|
|
245
|
+
for (const key of this.usedTokens.keys()) {
|
|
246
|
+
if (deleted >= evictCount)
|
|
247
|
+
break;
|
|
248
|
+
this.usedTokens.delete(key);
|
|
249
|
+
deleted++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (this.webhook) {
|
|
253
|
+
this.webhook.notify('approval_denied', updated);
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
approved: false,
|
|
257
|
+
approval_id: approval.approval_id,
|
|
258
|
+
resolved_by: approverId,
|
|
259
|
+
denial_reason: reason,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
this.inFlightApprovals.delete(approval.approval_id);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Resolve an approval by its ID (for admin panel use where the raw token is
|
|
268
|
+
* not available). Validates the approval is still pending and not expired.
|
|
269
|
+
*/
|
|
270
|
+
async resolveById(approvalId, approverId, approved, reason, approverApiKeyId) {
|
|
271
|
+
// In-process lock: prevent concurrent resolution of the same approval
|
|
272
|
+
if (this.inFlightApprovals.has(approvalId)) {
|
|
273
|
+
throw new Error(`Approval "${approvalId}" is already being processed by another request`);
|
|
274
|
+
}
|
|
275
|
+
this.inFlightApprovals.add(approvalId);
|
|
276
|
+
try {
|
|
277
|
+
const approval = this.store.getById(approvalId);
|
|
278
|
+
if (!approval) {
|
|
279
|
+
throw new Error('Approval not found');
|
|
280
|
+
}
|
|
281
|
+
if (approval.status === 'approved') {
|
|
282
|
+
throw new Error(`Approval "${approvalId}" has already been approved`);
|
|
283
|
+
}
|
|
284
|
+
if (approval.status === 'denied') {
|
|
285
|
+
throw new Error(`Approval "${approvalId}" has already been denied`);
|
|
286
|
+
}
|
|
287
|
+
if (approval.status === 'expired' || new Date() > new Date(approval.expires_at)) {
|
|
288
|
+
approval.status = 'expired';
|
|
289
|
+
throw new Error(`Approval "${approvalId}" has expired`);
|
|
290
|
+
}
|
|
291
|
+
// Prevent self-approval via API key comparison (cryptographic identity)
|
|
292
|
+
if (approved && approval.requesting_api_key_id && approverApiKeyId) {
|
|
293
|
+
const approverKeyHash = hashToken(approverApiKeyId);
|
|
294
|
+
if (approverKeyHash === approval.requesting_api_key_id) {
|
|
295
|
+
console.warn(`[ApprovalManager] Self-approval attempt blocked: approver=${approverId}, approval=${approvalId}, workspace=${approval.workspace_id}`);
|
|
296
|
+
throw new Error('Self-approval is not allowed. A different API key must approve this request.');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (approved && !approval.requesting_api_key_id && !approverApiKeyId) {
|
|
300
|
+
// Fallback: compare actor IDs when no API key is available (admin panel use)
|
|
301
|
+
if (approval.actor_id === approverId) {
|
|
302
|
+
console.warn(`[ApprovalManager] Self-approval attempt blocked (actor match): actor=${approverId}, approval=${approvalId}, workspace=${approval.workspace_id}`);
|
|
303
|
+
throw new Error('Self-approval is not allowed. A different user must approve this request.');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Actor-level self-approval check for API key path too
|
|
307
|
+
if (approved && approval.actor_id && approverId && approval.actor_id === approverId) {
|
|
308
|
+
console.warn(`[ApprovalManager] Self-approval attempt blocked (actor match): actor=${approverId}, approval=${approvalId}, workspace=${approval.workspace_id}`);
|
|
309
|
+
throw new Error('Self-approval is not allowed. A different user must approve this request.');
|
|
310
|
+
}
|
|
311
|
+
// Build updated approval object (do NOT mutate the original before CAS)
|
|
312
|
+
const updated = approved
|
|
313
|
+
? { ...approval, status: 'approved', resolved_at: new Date().toISOString(), resolved_by: approverId }
|
|
314
|
+
: { ...approval, status: 'denied', resolved_at: new Date().toISOString(), resolved_by: approverId, denial_reason: reason || 'Denied' };
|
|
315
|
+
// Atomic CAS: use compareAndSetStatus if available
|
|
316
|
+
if (this.store.compareAndSetStatus) {
|
|
317
|
+
const swapped = await this.store.compareAndSetStatus(approvalId, 'pending', updated);
|
|
318
|
+
if (!swapped) {
|
|
319
|
+
throw new Error(`Approval "${approvalId}" was already resolved by another request`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
this.store.save(approvalId, updated);
|
|
324
|
+
}
|
|
325
|
+
if (this.webhook) {
|
|
326
|
+
this.webhook.notify(approved ? 'approval_approved' : 'approval_denied', updated);
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
approved,
|
|
330
|
+
approval_id: approvalId,
|
|
331
|
+
resolved_by: approverId,
|
|
332
|
+
denial_reason: approved ? undefined : reason,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
finally {
|
|
336
|
+
this.inFlightApprovals.delete(approvalId);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Check if an approval exists and return its current status.
|
|
341
|
+
* Automatically marks the approval as 'expired' if it has passed its TTL.
|
|
342
|
+
*/
|
|
343
|
+
getApproval(approvalId) {
|
|
344
|
+
const approval = this.store.getById(approvalId);
|
|
345
|
+
if (!approval)
|
|
346
|
+
return null;
|
|
347
|
+
// Check if expired
|
|
348
|
+
if (approval.status === 'pending' && new Date() > new Date(approval.expires_at)) {
|
|
349
|
+
approval.status = 'expired';
|
|
350
|
+
}
|
|
351
|
+
return approval;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Look up an approval by the original tool_call_id. This is useful when an
|
|
355
|
+
* agent retries a tool call and needs to find the approval that was created
|
|
356
|
+
* for the original attempt.
|
|
357
|
+
*
|
|
358
|
+
* Automatically marks the approval as 'expired' if it has passed its TTL.
|
|
359
|
+
*/
|
|
360
|
+
getApprovalByToolCallId(toolCallId) {
|
|
361
|
+
const approval = this.store.getByToolCallId(toolCallId);
|
|
362
|
+
if (!approval)
|
|
363
|
+
return null;
|
|
364
|
+
// Check expiry
|
|
365
|
+
if (approval.status === 'pending' && new Date() > new Date(approval.expires_at)) {
|
|
366
|
+
approval.status = 'expired';
|
|
367
|
+
}
|
|
368
|
+
return approval;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Find an existing approved approval for the same task+actor+tool combination.
|
|
372
|
+
* Used to bypass re-approval when an agent retries after a previous approval.
|
|
373
|
+
*/
|
|
374
|
+
findApprovedForTask(taskId, actorId, toolName, capability) {
|
|
375
|
+
if (this.store.findApproved) {
|
|
376
|
+
return this.store.findApproved(taskId, actorId, toolName, capability) || null;
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Get all currently pending approvals, optionally filtered by workspace.
|
|
382
|
+
* Expires any approvals that are past their TTL before returning results.
|
|
383
|
+
*/
|
|
384
|
+
getPendingApprovals(workspaceId) {
|
|
385
|
+
const now = new Date();
|
|
386
|
+
const candidates = this.store.findPending(workspaceId);
|
|
387
|
+
const results = [];
|
|
388
|
+
for (const approval of candidates) {
|
|
389
|
+
// Expire any that are past TTL
|
|
390
|
+
if (approval.status === 'pending' && now > new Date(approval.expires_at)) {
|
|
391
|
+
approval.status = 'expired';
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
results.push(approval);
|
|
395
|
+
}
|
|
396
|
+
return results;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Verify and decode a JWT approval token.
|
|
400
|
+
* Returns the decoded payload on success, or an error message on failure.
|
|
401
|
+
*/
|
|
402
|
+
verifyToken(token) {
|
|
403
|
+
try {
|
|
404
|
+
const payload = jwt.verify(token, this.config.token_secret);
|
|
405
|
+
return { valid: true, payload };
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
const message = err instanceof Error ? err.message : 'Invalid token';
|
|
409
|
+
return { valid: false, error: message };
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Common logic shared by approve() and deny(): verify the token, look up
|
|
414
|
+
* the approval record, and validate that it can still be resolved.
|
|
415
|
+
*
|
|
416
|
+
* Returns the mutable PendingApproval record so the caller can update its
|
|
417
|
+
* status fields directly.
|
|
418
|
+
*
|
|
419
|
+
* Throws an Error if:
|
|
420
|
+
* - The token is invalid or has an expired signature
|
|
421
|
+
* - No approval record is found for the token
|
|
422
|
+
* - The approval has already been resolved (approved/denied)
|
|
423
|
+
* - The approval has expired (past TTL)
|
|
424
|
+
*/
|
|
425
|
+
resolveTokenToApproval(token) {
|
|
426
|
+
// Step 1: Verify the JWT signature and expiry
|
|
427
|
+
const verification = this.verifyToken(token);
|
|
428
|
+
if (!verification.valid) {
|
|
429
|
+
throw new Error(`Invalid approval token: ${verification.error}`);
|
|
430
|
+
}
|
|
431
|
+
// Step 2: Look up the approval record by token hash
|
|
432
|
+
const tokenHash = hashToken(token);
|
|
433
|
+
const approval = this.store.getByToken(tokenHash);
|
|
434
|
+
if (!approval) {
|
|
435
|
+
throw new Error('Approval not found for the provided token');
|
|
436
|
+
}
|
|
437
|
+
const approvalId = approval.approval_id;
|
|
438
|
+
// Step 3: Check if the approval has already been resolved
|
|
439
|
+
if (approval.status === 'approved') {
|
|
440
|
+
throw new Error(`Approval "${approvalId}" has already been approved`);
|
|
441
|
+
}
|
|
442
|
+
if (approval.status === 'denied') {
|
|
443
|
+
throw new Error(`Approval "${approvalId}" has already been denied`);
|
|
444
|
+
}
|
|
445
|
+
// Step 4: Check if the approval has expired (belt-and-suspenders with JWT expiry)
|
|
446
|
+
if (approval.status === 'expired' || new Date() > new Date(approval.expires_at)) {
|
|
447
|
+
approval.status = 'expired';
|
|
448
|
+
throw new Error(`Approval "${approvalId}" has expired`);
|
|
449
|
+
}
|
|
450
|
+
return approval;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Create a sanitized summary of the tool call arguments that is safe to
|
|
454
|
+
* display to an approver. Strips query parameters (may contain tokens or
|
|
455
|
+
* secrets), omits headers entirely, and only indicates whether a body is
|
|
456
|
+
* present without revealing its contents.
|
|
457
|
+
*/
|
|
458
|
+
sanitizeArgs(toolCall) {
|
|
459
|
+
const { method, url, body } = toolCall.args;
|
|
460
|
+
const parts = [];
|
|
461
|
+
if (method)
|
|
462
|
+
parts.push(`method=${method}`);
|
|
463
|
+
if (url) {
|
|
464
|
+
// Only include domain and path, not full URL (might contain sensitive query params)
|
|
465
|
+
try {
|
|
466
|
+
const parsed = new URL(url);
|
|
467
|
+
parts.push(`domain=${parsed.hostname}`);
|
|
468
|
+
parts.push(`path=${parsed.pathname}`);
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
parts.push(`url=[invalid]`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (body)
|
|
475
|
+
parts.push(`body=[present]`);
|
|
476
|
+
return parts.join(', ') || 'no args';
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Scan all pending approvals and mark any that have passed their TTL as
|
|
480
|
+
* 'expired'. Returns the number of approvals that were expired.
|
|
481
|
+
*/
|
|
482
|
+
cleanup() {
|
|
483
|
+
let cleaned = 0;
|
|
484
|
+
const now = new Date();
|
|
485
|
+
const pending = this.store.findPending();
|
|
486
|
+
for (const approval of pending) {
|
|
487
|
+
if (now > new Date(approval.expires_at)) {
|
|
488
|
+
approval.status = 'expired';
|
|
489
|
+
cleaned++;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return cleaned;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Wait for all pending store writes to complete.
|
|
496
|
+
*/
|
|
497
|
+
async flush() {
|
|
498
|
+
if (this.store.flush)
|
|
499
|
+
await this.store.flush();
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Clear all approval state. Intended for use in tests only.
|
|
503
|
+
*/
|
|
504
|
+
clear() {
|
|
505
|
+
this.store.clear();
|
|
506
|
+
this.inFlightApprovals.clear();
|
|
507
|
+
this.usedTokens.clear();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
exports.ApprovalManager = ApprovalManager;
|
|
511
|
+
//# sourceMappingURL=manager.js.map
|