@desplega.ai/agent-swarm 1.165.0 → 1.167.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/dist/acp-adapter-zpvy6321.js +34 -0
- package/dist/{actions-jna0b3wm.js → actions-fq2zrwtf.js} +30 -34
- package/dist/{amp-adapter-0eve81pz.js → amp-adapter-5gbk7501.js} +7 -7
- package/dist/{anthropic-messages-hst62ws9.js → anthropic-messages-13yn502s.js} +4 -4
- package/dist/{app-75hm5cfa.js → app-nctetn1e.js} +11 -11
- package/dist/{assistant-yzd51j33.js → assistant-0aq8gjvj.js} +31 -35
- package/dist/{azure-openai-responses-mh8tvf78.js → azure-openai-responses-9st34pfz.js} +5 -5
- package/dist/{boot-reembed-z158xq32.js → boot-reembed-1ze5853n.js} +14 -16
- package/dist/{boot-reembed-2ex2mcsh.js → boot-reembed-fmp5ww69.js} +15 -16
- package/dist/boot-scrub-sweep-cbp8xd4e.js +212 -0
- package/dist/{boot-seeds-2bmnh18p.js → boot-seeds-22tx8a53.js} +13 -12
- package/dist/{claude-adapter-jbj1mycz.js → claude-adapter-0jhgeh1d.js} +25 -25
- package/dist/{claude-managed-adapter-17ggz09t.js → claude-managed-adapter-tzwxy803.js} +5 -5
- package/dist/{claude-managed-setup-ptm1nzm2.js → claude-managed-setup-nrhfx5b0.js} +5 -5
- package/dist/{claude-sdk-session-9re1g7r2.js → claude-sdk-session-wh77rh83.js} +2632 -2614
- package/dist/{cli-zsh093hk.js → cli-014vns2y.js} +63 -1
- package/dist/{cli-qvfk83fy.js → cli-13xwcqvj.js} +2 -2
- package/dist/{cli-8gpbwy09.js → cli-1yrcfa5s.js} +5 -5
- package/dist/{cli-xtgtyafv.js → cli-577bxkf6.js} +212 -451
- package/dist/{cli-ma3zvh39.js → cli-5qkcdv8c.js} +6118 -5874
- package/dist/{cli-cwxhbf43.js → cli-5t5my0h8.js} +85 -2
- package/dist/{cli-zvckv750.js → cli-69egj654.js} +2 -2
- package/dist/{cli-9xb89xca.js → cli-6bpvv8h4.js} +9 -9
- package/dist/{cli-90v942q1.js → cli-6qxmjpem.js} +8 -8
- package/dist/{cli-9866bd07.js → cli-6vfz44sr.js} +1 -1
- package/dist/{cli-32bca7nr.js → cli-7avy4v8w.js} +1 -1
- package/dist/{cli-xb3c6aat.js → cli-7mhy3ths.js} +21 -18
- package/dist/{cli-q38n4bq0.js → cli-95zs84qw.js} +3 -3
- package/dist/cli-a35hzdve.js +33 -0
- package/dist/{cli-x3x0qwky.js → cli-bxqez1p0.js} +51 -23
- package/dist/{cli-prt9emct.js → cli-c2tpw9ck.js} +3 -3
- package/dist/{cli-0n4fasz0.js → cli-c8r99akr.js} +16 -16
- package/dist/{cli-5anb0ev3.js → cli-cexctgn5.js} +89 -26
- package/dist/{cli-bns5c8k9.js → cli-cf2vgn9d.js} +105 -91
- package/dist/{cli-ng97fjq7.js → cli-cmxkbzzs.js} +33 -7
- package/dist/{cli-nexwh8zg.js → cli-g7k7zbcz.js} +12 -5
- package/dist/{cli-mmzwwptd.js → cli-hf5s9pgf.js} +1 -1
- package/dist/{cli-nvvfd8bq.js → cli-jbzbr36n.js} +4091 -3793
- package/dist/{cli-zn2v3t8v.js → cli-jvmtxqtc.js} +27 -15
- package/dist/{cli-pkke6geq.js → cli-km4x66j0.js} +83 -65
- package/dist/cli-kw0jcfje.js +516 -0
- package/dist/{cli-28smfvbn.js → cli-m7vmeepg.js} +32 -19
- package/dist/{cli-hpryr6em.js → cli-mb75wpxf.js} +1 -1
- package/dist/{cli-qdv8kgz7.js → cli-mkdsy1nq.js} +14018 -3333
- package/dist/{cli-b0f70z24.js → cli-mn8qkd8v.js} +1 -1
- package/dist/{cli-420bdgw0.js → cli-mv2520nm.js} +1 -1
- package/dist/{cli-n9ehc28t.js → cli-mxry91sa.js} +31 -10
- package/dist/{cli-wfajtmf7.js → cli-mxvnnv5q.js} +596 -13
- package/dist/cli-nkqjj42q.js +104 -0
- package/dist/{cli-e7t4snpa.js → cli-p8rt8spp.js} +5 -5
- package/dist/{cli-gh5yqzpe.js → cli-pkj1w46r.js} +12 -7
- package/dist/cli-q1vnz8wv.js +96 -0
- package/dist/{cli-fj86xr3c.js → cli-qd97v6z2.js} +2 -2
- package/dist/{cli-ba66wyhy.js → cli-qzmzwegq.js} +60 -20
- package/dist/{cli-5b2fjjzh.js → cli-r7eeddkq.js} +14 -9
- package/dist/{cli-8ssjez7e.js → cli-rhy7z1rp.js} +22 -11
- package/dist/{cli-4mevd1hf.js → cli-s9y81s7j.js} +5 -5
- package/dist/{cli-59jn5twa.js → cli-tc5zec7v.js} +7 -7
- package/dist/{cli-ttbsse3f.js → cli-tmhsxhb8.js} +1 -1
- package/dist/{cli-k6xx3pve.js → cli-tx2zvn23.js} +4 -4
- package/dist/{cli-pjxnr1jn.js → cli-ty4bzkqr.js} +5 -5
- package/dist/{cli-nfygw434.js → cli-v1pwxreq.js} +55 -52
- package/dist/{cli-ftry7975.js → cli-v1w7pbd8.js} +3 -2
- package/dist/{cli-10zmp1p7.js → cli-vr5avem2.js} +27 -32
- package/dist/{cli-bzr0ftht.js → cli-wnb9b2g3.js} +19 -0
- package/dist/cli-wt7ccy4r.js +2654 -0
- package/dist/{cli-50eg13rj.js → cli-wydz3feg.js} +549 -321
- package/dist/{cli-krqm5f11.js → cli-xezn9nkh.js} +16 -13
- package/dist/{cli-8kt0bmgx.js → cli-xf713zz0.js} +2 -2
- package/dist/{cli-x7afk5wm.js → cli-xvc3q9br.js} +1 -1
- package/dist/{cli-9s3h836g.js → cli-xvdgasgy.js} +1 -1
- package/dist/{cli-j0pdrmwg.js → cli-y0kjpfm9.js} +3 -3
- package/dist/{cli-6zcfw0x7.js → cli-y195hv1a.js} +1 -1
- package/dist/{cli-amwhs8h2.js → cli-ykt8f7va.js} +22 -9
- package/dist/{cli-1kzfvd1z.js → cli-ymmc1zxg.js} +15 -11
- package/dist/{cli-xnzrzets.js → cli-ypgba1a8.js} +2 -2
- package/dist/{cli-bdv39kgj.js → cli-z1dqhc9y.js} +31 -20
- package/dist/{cli-fw6vdcq5.js → cli-zp67qg5x.js} +34 -1
- package/dist/{cli-qbksc292.js → cli-zy39d433.js} +2 -0
- package/dist/cli.js +36 -32
- package/dist/{cloudflare-workers-ai-system-one-tt484t61.js → cloudflare-workers-ai-system-one-sta45jf9.js} +7 -5
- package/dist/{codex-adapter-3qnxshje.js → codex-adapter-cgesnjkr.js} +20 -20
- package/dist/{codex-login-brkxhhdg.js → codex-login-g1cyt5zm.js} +2 -2
- package/dist/{codex-session-runner-612wnapp.js → codex-session-runner-8kd7ddhr.js} +20 -20
- package/dist/{commands-2ybfcxkj.js → commands-h6zaaxym.js} +11 -10
- package/dist/{cursor-adapter-p3jgdehv.js → cursor-adapter-v8mm6bp5.js} +7 -7
- package/dist/{db-f4ans0jj.js → db-qg3myn53.js} +13 -10
- package/dist/{devin-adapter-dhzt01ff.js → devin-adapter-bffjj5n4.js} +2 -2
- package/dist/{dispatcher-pk182apd.js → dispatcher-7vnwj9qw.js} +28 -32
- package/dist/{dsh-adapter-7twaeps1.js → dsh-adapter-tz6hfp5r.js} +7 -7
- package/dist/{e2b-y8t3jzyz.js → e2b-nd50eccg.js} +1 -1
- package/dist/{easter-egg-3d-mtpnw06s.js → easter-egg-3d-66zxervv.js} +2 -2
- package/dist/{event-bus-w7ac4n3c.js → event-bus-vnfthafg.js} +2 -2
- package/dist/{execute-s4ccjq3n.js → execute-h4jgevyw.js} +61 -11
- package/dist/{google-generative-ai-d6nphg3q.js → google-generative-ai-b8zxrpax.js} +5 -5
- package/dist/{google-vertex-cpshkref.js → google-vertex-nrdd697t.js} +5 -5
- package/dist/grok-adapter-jdt2dz2s.js +39 -0
- package/dist/{handlers-g2wnr4t9.js → handlers-2qtkpncr.js} +31 -35
- package/dist/{hook-f7jt9vnm.js → hook-51yfcgsv.js} +48 -39
- package/dist/{hook-bbdpnkb4.js → hook-qnbdgwvd.js} +15 -17
- package/dist/{http-w40q4w94.js → http-jsmtag08.js} +536 -231
- package/dist/{index-056j5kzn.js → index-2t9kesn1.js} +13 -12
- package/dist/{index-cj1r9q83.js → index-bt8wkv1w.js} +29 -33
- package/dist/{index-7n70jwbb.js → index-cx1sb9bq.js} +42 -35
- package/dist/{index-9bfssf54.js → index-ekywyv2v.js} +28 -32
- package/dist/{index-fdk1t4d6.js → index-n0rxa4ca.js} +11962 -9900
- package/dist/{index-k8xc083e.js → index-t4g06w93.js} +28 -32
- package/dist/{keepalive-1tj10tf4.js → keepalive-4aezx4bj.js} +16 -12
- package/dist/{lead-st0bg7v4.js → lead-r44bkpm5.js} +52 -56
- package/dist/{llama-cpp-classify-tgj35k8d.js → llama-cpp-classify-6erb7wwv.js} +3 -1
- package/dist/{maintenance-nz71zats.js → maintenance-grz5jrzh.js} +12 -13
- package/dist/{mistral-conversations-8445ddjn.js → mistral-conversations-059y51zt.js} +4 -4
- package/dist/{node-f0ykebbv.js → node-qm9eb6z0.js} +25 -7
- package/dist/{oauth-refresh-sweep-aefzwvvq.js → oauth-refresh-sweep-wm4ctjfj.js} +15 -11
- package/dist/{onboard-kgpr9mbn.js → onboard-v60zgqf0.js} +2 -2
- package/dist/{openai-codex-responses-9ynm88zy.js → openai-codex-responses-jzxkta9b.js} +8 -7
- package/dist/{openai-completions-nhqwjmaw.js → openai-completions-663yncfq.js} +4 -4
- package/dist/openai-decisions-vj9nkpf8.js +162 -0
- package/dist/{openai-responses-xx1c4kgm.js → openai-responses-m3g43rrt.js} +5 -5
- package/dist/{opencode-adapter-0c8e70dk.js → opencode-adapter-yjyqynzf.js} +8 -8
- package/dist/{openrouter-images-8yx3487g.js → openrouter-images-kr60n6b9.js} +1 -1
- package/dist/{otel-impl-x8zp4y5a.js → otel-impl-ghh4dyf8.js} +2 -2
- package/dist/{pi-messages-vxdt8v7j.js → pi-messages-xszk613n.js} +1 -1
- package/dist/{pi-mono-adapter-t280ah5c.js → pi-mono-adapter-0eg8kw2c.js} +69 -52
- package/dist/{pricing-refresh-9e0q7cwk.js → pricing-refresh-mjrqvnvw.js} +12 -11
- package/dist/{rbac-roles-790j3ga8.js → rbac-roles-axv4d74x.js} +11 -10
- package/dist/{rbac-roles-kcp6zge2.js → rbac-roles-fd5tskj9.js} +11 -10
- package/dist/{render-v2-tch4nefj.js → render-v2-k50j33qd.js} +11 -11
- package/dist/{runtime-v2t1r79e.js → runtime-h9ha1mq6.js} +4 -4
- package/dist/{setup-rcm303ns.js → setup-4syh0pd2.js} +2 -2
- package/dist/{typesafe-system-one-datfr82p.js → typesafe-system-one-e988vty7.js} +7 -5
- package/dist/{virtual-modules-wm8mxdxx.js → virtual-modules-ftr9kwe8.js} +11 -11
- package/dist/{work-objects-ctg7t70w.js → work-objects-jcdbg8rr.js} +32 -36
- package/dist/{worker-5dp0xm55.js → worker-x8j8c9sx.js} +52 -56
- package/dist/{x-v3yw66zs.js → x-ma4gnfgy.js} +2 -2
- package/openapi.json +5 -2
- package/package.json +15 -11
- package/src/be/automation-preflight.ts +9 -4
- package/src/be/boot-scrub-sweep.ts +302 -0
- package/src/be/crypto/key-bootstrap.ts +1 -1
- package/src/be/db/approvals.ts +15 -1
- package/src/be/db/codex-oauth-device-flows.ts +40 -0
- package/src/be/db/config-secrets.ts +31 -0
- package/src/be/db/runtime.ts +4 -0
- package/src/be/db/script-runs.ts +15 -0
- package/src/be/db/tasks/write.ts +1 -1
- package/src/be/db-queries/mcp-oauth.ts +5 -0
- package/src/be/db-queries/oauth.ts +77 -0
- package/src/be/db.ts +162 -59
- package/src/be/events.ts +17 -1
- package/src/be/memory/index-content.ts +2 -2
- package/src/be/memory/providers/openai-embedding.ts +7 -2
- package/src/be/memory/providers/sqlite-store.ts +63 -11
- package/src/be/memory/raters/llm-summarizer.ts +7 -2
- package/src/be/memory/types.ts +14 -0
- package/src/be/migrations/201_workflow_replay_columns.sql +10 -0
- package/src/be/migrations/202_codex_oauth_device_flows.sql +24 -0
- package/src/be/modelsdev-cache.json +61 -0
- package/src/be/scripts/db.ts +28 -4
- package/src/be/scripts/source-secrets.ts +37 -0
- package/src/be/scrub-json.ts +32 -0
- package/src/be/sealed-json.ts +59 -0
- package/src/be/secret-registry.ts +131 -0
- package/src/be/seed-pricing.ts +84 -53
- package/src/be/swarm-config-guard.ts +13 -0
- package/src/be/workflow-replay.ts +77 -0
- package/src/commands/provider-credentials.ts +28 -5
- package/src/commands/runner.ts +7 -12
- package/src/heartbeat/heartbeat.ts +91 -19
- package/src/hooks/hook.ts +72 -27
- package/src/http/codex-oauth-device.ts +8 -10
- package/src/http/db-query-bounded.ts +3 -0
- package/src/http/db-query-shared.ts +107 -15
- package/src/http/db-query.ts +10 -2
- package/src/http/index.ts +23 -6
- package/src/http/mcp.ts +5 -0
- package/src/http/onboarding.ts +2 -0
- package/src/http/script-runs.ts +9 -1
- package/src/http/scripts.ts +15 -0
- package/src/http/workflows.ts +3 -2
- package/src/oauth/mcp-wrapper.ts +1 -1
- package/src/providers/acp-adapter.ts +25 -11
- package/src/providers/acp-target-catalog.ts +35 -1
- package/src/providers/acp-targets.ts +78 -7
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +26 -0
- package/src/providers/codex-adapter.ts +30 -3
- package/src/providers/codex-oauth/auth-json.ts +21 -4
- package/src/providers/codex-oauth/types.ts +2 -0
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +10 -1
- package/src/providers/grok-adapter.ts +18 -2
- package/src/providers/opencode-adapter.ts +21 -5
- package/src/providers/pi-mono-extension.ts +56 -25
- package/src/providers/pricing-sources.md +2 -0
- package/src/script-workflows/supervisor.ts +11 -2
- package/src/scripts-runtime/eval-harness.ts +2 -12
- package/src/scripts-runtime/executors/quickjs-bundle.ts +152 -0
- package/src/scripts-runtime/executors/quickjs-prelude.ts +285 -0
- package/src/scripts-runtime/executors/quickjs-runner.ts +580 -0
- package/src/scripts-runtime/executors/quickjs-worker.ts +81 -0
- package/src/scripts-runtime/executors/quickjs.ts +196 -0
- package/src/scripts-runtime/executors/registry.ts +2 -0
- package/src/scripts-runtime/executors/sourcemap.ts +79 -0
- package/src/scripts-runtime/script-error-hints.ts +14 -0
- package/src/server.ts +66 -4
- package/src/tests/acp-adapter.test.ts +277 -12
- package/src/tests/boot-scrub-sweep.test.ts +390 -0
- package/src/tests/check-text-columns.test.ts +145 -0
- package/src/tests/ci-unit-test-report.test.ts +73 -0
- package/src/tests/claude-default-route.test.ts +75 -0
- package/src/tests/claude-hook-output.test.ts +233 -0
- package/src/tests/claude-sdk-process.fixture.ts +13 -2
- package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +4 -2
- package/src/tests/codex-adapter.test.ts +166 -26
- package/src/tests/codex-oauth-auth-timestamps.test.ts +76 -0
- package/src/tests/codex-oauth-device.test.ts +23 -37
- package/src/tests/codex-oauth.test.ts +4 -1
- package/src/tests/context-window.test.ts +2 -2
- package/src/tests/credential-check.test.ts +51 -1
- package/src/tests/db-query-denylist.test.ts +202 -0
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/db-task-write-contract.test.ts +1 -0
- package/src/tests/dsh-adapter.test.ts +23 -1
- package/src/tests/enabled-tools.test.ts +269 -0
- package/src/tests/entrypoint-acp-target-binary.test.ts +31 -1
- package/src/tests/entrypoint-codex-oauth-seed.test.ts +12 -0
- package/src/tests/events-scrub.test.ts +88 -0
- package/src/tests/fixtures/secret-corpus.ts +320 -0
- package/src/tests/grok-adapter.test.ts +13 -0
- package/src/tests/haiku-5-5-pricing.test.ts +38 -0
- package/src/tests/heartbeat-checklist.test.ts +158 -1
- package/src/tests/onboarding.test.ts +44 -0
- package/src/tests/opencode-adapter.test.ts +66 -3
- package/src/tests/opencode-plugin.test.ts +37 -0
- package/src/tests/openrouter-attribution-call-sites.test.ts +204 -0
- package/src/tests/openrouter-base-url.test.ts +30 -39
- package/src/tests/pi-mono-extension.test.ts +161 -2
- package/src/tests/pricing-refresh.test.ts +70 -1
- package/src/tests/reasoning-effort.test.ts +3 -1
- package/src/tests/script-connections.test.ts +5 -0
- package/src/tests/script-executor-conformance.test.ts +3 -0
- package/src/tests/script-executor-quickjs.test.ts +442 -0
- package/src/tests/script-executor-registry.test.ts +6 -1
- package/src/tests/script-runs-http.test.ts +88 -1
- package/src/tests/script-runs-scrub.test.ts +275 -0
- package/src/tests/scripts-http.test.ts +82 -1
- package/src/tests/scrubbed-text-12e.test.ts +289 -0
- package/src/tests/sealed-key-guard.test.ts +166 -0
- package/src/tests/secret-canary-sweep.test.ts +958 -0
- package/src/tests/secret-registry.test.ts +355 -0
- package/src/tests/secret-rules.test.ts +230 -0
- package/src/tests/secret-scrubber.test.ts +3 -2
- package/src/tests/telemetry-trigger.test.ts +12 -1
- package/src/tests/workflow-llm.test.ts +14 -0
- package/src/tests/workflow-replay.test.ts +518 -0
- package/src/tests/workflow-scrub.test.ts +242 -0
- package/src/tests/workflow-system-one-decision-providers.test.ts +33 -0
- package/src/tools/db-query.ts +1 -1
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/request-human-input.ts +7 -2
- package/src/tools/workflows/get-workflow-run.ts +3 -2
- package/src/types.ts +1 -1
- package/src/utils/enabled-tools.ts +27 -0
- package/src/utils/internal-ai/complete-structured.ts +11 -3
- package/src/utils/openrouter-base-url.ts +23 -0
- package/src/utils/secret-rules.generated.ts +2055 -0
- package/src/utils/secret-scrubber.ts +363 -29
- package/src/workflows/engine.ts +39 -4
- package/src/workflows/executors/raw-llm.ts +1 -0
- package/src/workflows/executors/system-one-decision.ts +2 -0
- package/src/workflows/executors/validate.ts +1 -0
- package/src/workflows/executors/workflow-llm.ts +9 -2
- package/src/workflows/recovery.ts +10 -1
- package/src/workflows/resume.ts +62 -3
- package/src/workflows/retry-poller.ts +3 -0
- package/templates/skills/agent-browser/SKILL.md +2 -0
- package/templates/skills/agent-browser/content.md +2 -0
- package/templates/skills/code-quality/content.md +1 -0
- package/templates/skills/heartbeat-runbook/content.md +1 -1
- package/templates/skills/work-on-task/SKILL.md +4 -0
- package/templates/skills/work-on-task/content.md +4 -0
- package/templates/skills/workflow-iterate/content.md +9 -0
- package/dist/acp-adapter-w6zws6me.js +0 -23
- package/dist/boot-scrub-logs-246yn076.js +0 -99
- package/dist/cli-3a4m1aws.js +0 -572
- package/dist/cli-3dpj7atn.js +0 -11
- package/dist/cli-a87b04k8.js +0 -951
- package/dist/cli-cvk6yzw5.js +0 -40
- package/dist/cli-gvcgmx69.js +0 -183
- package/dist/cli-h1spv8v4.js +0 -427
- package/dist/cli-jqt9hrnz.js +0 -168
- package/dist/cli-yhmwmv0g.js +0 -9281
- package/dist/grok-adapter-yt9pab3k.js +0 -29
- package/src/be/boot-scrub-logs.ts +0 -132
- package/dist/{codex-hook-d2rkvf39.js → codex-hook-15dgc4cb.js} +1 -1
|
@@ -14,12 +14,17 @@ import {
|
|
|
14
14
|
initDb,
|
|
15
15
|
updateScriptRun,
|
|
16
16
|
} from "../be/db";
|
|
17
|
+
import { configSecretName, registerStoredSecret } from "../be/secret-registry";
|
|
17
18
|
import { handleCore } from "../http/core";
|
|
18
19
|
import { handleScriptRuns } from "../http/script-runs";
|
|
19
20
|
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
20
21
|
import { localProcessScriptExecutor } from "../script-workflows/executor";
|
|
21
22
|
import { setScriptRunExecutor } from "../script-workflows/supervisor";
|
|
22
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
clearVolatileSecretsForTesting,
|
|
25
|
+
refreshSecretScrubberCache,
|
|
26
|
+
} from "../utils/secret-scrubber";
|
|
27
|
+
import { randomToken } from "./synthetic-secret-helpers";
|
|
23
28
|
|
|
24
29
|
const TEST_DB_PATH = "./test-script-runs-http.sqlite";
|
|
25
30
|
const API_KEY = "example-test-script-runs-http-key-1234567890";
|
|
@@ -194,6 +199,11 @@ describe("/api/script-runs HTTP", () => {
|
|
|
194
199
|
body: createBody({ background: true }),
|
|
195
200
|
});
|
|
196
201
|
expect(created.status).toBe(201);
|
|
202
|
+
// The launch is fire-and-forget and reads the sealed args before it
|
|
203
|
+
// starts the executor, so it can land after the response.
|
|
204
|
+
for (let i = 0; i < 50 && spawnedWith.length === 0; i++) {
|
|
205
|
+
await Bun.sleep(20);
|
|
206
|
+
}
|
|
197
207
|
expect(spawnedWith).toEqual(["http://swarm-internal.example.test:3013"]);
|
|
198
208
|
} finally {
|
|
199
209
|
setScriptRunExecutor(localProcessScriptExecutor);
|
|
@@ -303,6 +313,54 @@ describe("/api/script-runs HTTP", () => {
|
|
|
303
313
|
});
|
|
304
314
|
});
|
|
305
315
|
|
|
316
|
+
test("redacts journal config/error and run output/error posted by the harness", async () => {
|
|
317
|
+
const token = ["ghp", randomToken(36)].join("_");
|
|
318
|
+
const created = await dispatch("/api/script-runs", {
|
|
319
|
+
method: "POST",
|
|
320
|
+
agentId,
|
|
321
|
+
body: createBody(),
|
|
322
|
+
});
|
|
323
|
+
const { id } = (await created.json()) as { id: string };
|
|
324
|
+
|
|
325
|
+
const recorded = await dispatch(`/api/internal/script-runs/${id}/steps`, {
|
|
326
|
+
method: "POST",
|
|
327
|
+
agentId,
|
|
328
|
+
body: JSON.stringify({
|
|
329
|
+
stepKey: "deploy",
|
|
330
|
+
stepType: "agent-task",
|
|
331
|
+
config: { task: `deploy with ${token}` },
|
|
332
|
+
status: "failed",
|
|
333
|
+
error: `deploy failed: bad credential ${token}`,
|
|
334
|
+
}),
|
|
335
|
+
});
|
|
336
|
+
expect(recorded.status).toBe(201);
|
|
337
|
+
const step = await getDbClient().get<{ config: string; error: string }>(
|
|
338
|
+
"SELECT config, error FROM script_run_journal WHERE runId = ?",
|
|
339
|
+
[id],
|
|
340
|
+
);
|
|
341
|
+
for (const value of [step?.config, step?.error]) {
|
|
342
|
+
expect(value).toBeString();
|
|
343
|
+
expect(value!.includes(token)).toBe(false);
|
|
344
|
+
expect(value!).toContain("[REDACTED:");
|
|
345
|
+
}
|
|
346
|
+
expect(step!.error).toContain("deploy failed: bad credential");
|
|
347
|
+
expect((JSON.parse(step!.config) as { task: string }).task).toContain("deploy with");
|
|
348
|
+
|
|
349
|
+
await updateScriptRun(id, { status: "running" });
|
|
350
|
+
const status = await dispatch(`/api/internal/script-runs/${id}/status`, {
|
|
351
|
+
method: "POST",
|
|
352
|
+
agentId,
|
|
353
|
+
body: JSON.stringify({ status: "failed", error: `exit: ${token}` }),
|
|
354
|
+
});
|
|
355
|
+
expect(status.status).toBe(204);
|
|
356
|
+
const run = await getDbClient().get<{ error: string }>(
|
|
357
|
+
"SELECT error FROM script_runs WHERE id = ?",
|
|
358
|
+
[id],
|
|
359
|
+
);
|
|
360
|
+
expect(run!.error.includes(token)).toBe(false);
|
|
361
|
+
expect(run!.error).toContain("exit: [REDACTED:");
|
|
362
|
+
});
|
|
363
|
+
|
|
306
364
|
test("aborts the run when the journal step cap is exceeded", async () => {
|
|
307
365
|
process.env.SCRIPT_RUN_MAX_STEPS = "1";
|
|
308
366
|
const created = await dispatch("/api/script-runs", {
|
|
@@ -574,3 +632,32 @@ describe("/api/script-runs HTTP", () => {
|
|
|
574
632
|
expect(body.run.error).toBe("original failure");
|
|
575
633
|
});
|
|
576
634
|
});
|
|
635
|
+
|
|
636
|
+
describe("durable launch source with an embedded secret", () => {
|
|
637
|
+
test("is refused before the run row is written", async () => {
|
|
638
|
+
const value = `durable-${randomToken(32)}`;
|
|
639
|
+
const name = configSecretName("DURABLE_TOKEN");
|
|
640
|
+
registerStoredSecret(value, name);
|
|
641
|
+
try {
|
|
642
|
+
const before = await getDbClient().get<{ n: number }>(
|
|
643
|
+
"SELECT COUNT(*) AS n FROM script_runs",
|
|
644
|
+
);
|
|
645
|
+
const response = await dispatch("/api/script-runs", {
|
|
646
|
+
method: "POST",
|
|
647
|
+
agentId,
|
|
648
|
+
body: createBody({
|
|
649
|
+
source: `export default async function main() { return { auth: "${value}" }; }`,
|
|
650
|
+
}),
|
|
651
|
+
});
|
|
652
|
+
expect(response.status).toBe(400);
|
|
653
|
+
expect(response.text).not.toContain(value);
|
|
654
|
+
const body = (await response.json()) as { error: string; findings: { id: string }[] };
|
|
655
|
+
expect(body.error).toBe("source_contains_secret");
|
|
656
|
+
expect(body.findings.map((f) => f.id)).toEqual([name]);
|
|
657
|
+
const after = await getDbClient().get<{ n: number }>("SELECT COUNT(*) AS n FROM script_runs");
|
|
658
|
+
expect(after?.n).toBe(before?.n);
|
|
659
|
+
} finally {
|
|
660
|
+
clearVolatileSecretsForTesting();
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
});
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import {
|
|
4
|
+
closeDb,
|
|
5
|
+
createScriptRun,
|
|
6
|
+
getDbClient,
|
|
7
|
+
getRunningScriptRuns,
|
|
8
|
+
getScriptRunJournalStep,
|
|
9
|
+
initDb,
|
|
10
|
+
listScriptRunJournalSteps,
|
|
11
|
+
recordInlineScriptRun,
|
|
12
|
+
updateScriptRun,
|
|
13
|
+
updateScriptRunIfNotTerminal,
|
|
14
|
+
updateScriptRunIfRunning,
|
|
15
|
+
upsertScriptRunJournalStep,
|
|
16
|
+
} from "../be/db";
|
|
17
|
+
import { breaksJsonValidity, scrubJsonValue } from "../be/scrub-json";
|
|
18
|
+
import { isSealedJson } from "../be/sealed-json";
|
|
19
|
+
import {
|
|
20
|
+
localProcessScriptExecutor,
|
|
21
|
+
type ScriptExecutionResult,
|
|
22
|
+
type StartScriptExecutionInput,
|
|
23
|
+
} from "../script-workflows/executor";
|
|
24
|
+
import { setScriptRunExecutor, startScriptRunProcess } from "../script-workflows/supervisor";
|
|
25
|
+
import { refreshSecretScrubberCache, scrubSecrets } from "../utils/secret-scrubber";
|
|
26
|
+
import { randomToken, type SyntheticSecret, syntheticSecret } from "./synthetic-secret-helpers";
|
|
27
|
+
|
|
28
|
+
const TEST_DB_PATH = "./test-script-runs-scrub.sqlite";
|
|
29
|
+
const AGENT_ID = "5c2b0000-0000-4000-8000-0000000012c0";
|
|
30
|
+
// Distinctive non-secret context that must survive redaction.
|
|
31
|
+
const CONTEXT = "wombatlantern";
|
|
32
|
+
|
|
33
|
+
let known: SyntheticSecret;
|
|
34
|
+
let ghToken: string;
|
|
35
|
+
|
|
36
|
+
async function removeDbFiles(): Promise<void> {
|
|
37
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
38
|
+
try {
|
|
39
|
+
await unlink(TEST_DB_PATH + suffix);
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Free text carrying a registered known value and a structural GitHub token. */
|
|
45
|
+
function secretText(label: string): string {
|
|
46
|
+
return `${label} ${CONTEXT} known=${known.value} gh=${ghToken} end`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function expectRedacted(value: string | null | undefined, where: string): void {
|
|
50
|
+
expect(value, where).toBeString();
|
|
51
|
+
expect(value!.includes(known.value), `${where} still holds the known value`).toBe(false);
|
|
52
|
+
expect(value!.includes(ghToken), `${where} still holds the token`).toBe(false);
|
|
53
|
+
expect(value!, where).toContain("[REDACTED:");
|
|
54
|
+
expect(value!, where).toContain(CONTEXT);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function runRow(id: string) {
|
|
58
|
+
return getDbClient().get<{ args: string; output: string | null; error: string | null }>(
|
|
59
|
+
"SELECT args, output, error FROM script_runs WHERE id = ?",
|
|
60
|
+
[id],
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function seedRun(): Promise<string> {
|
|
65
|
+
const id = crypto.randomUUID();
|
|
66
|
+
await createScriptRun({ id, agentId: AGENT_ID, source: "export default () => 1", args: null });
|
|
67
|
+
await updateScriptRun(id, { status: "running" });
|
|
68
|
+
return id;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe("script run writers scrub at write", () => {
|
|
72
|
+
beforeAll(async () => {
|
|
73
|
+
await removeDbFiles();
|
|
74
|
+
initDb(TEST_DB_PATH);
|
|
75
|
+
ghToken = ["ghp", randomToken(36)].join("_");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterAll(async () => {
|
|
79
|
+
known.cleanup();
|
|
80
|
+
refreshSecretScrubberCache();
|
|
81
|
+
closeDb();
|
|
82
|
+
await removeDbFiles();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
beforeEach(async () => {
|
|
86
|
+
// The test preload clears volatile secrets after every test.
|
|
87
|
+
known = syntheticSecret("scriptrun");
|
|
88
|
+
refreshSecretScrubberCache();
|
|
89
|
+
await getDbClient().run("DELETE FROM script_run_journal");
|
|
90
|
+
await getDbClient().run("DELETE FROM script_runs");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("recordInlineScriptRun redacts args, output and error", async () => {
|
|
94
|
+
const id = crypto.randomUUID();
|
|
95
|
+
const now = new Date().toISOString();
|
|
96
|
+
await recordInlineScriptRun({
|
|
97
|
+
id,
|
|
98
|
+
agentId: AGENT_ID,
|
|
99
|
+
source: "export default () => 1",
|
|
100
|
+
args: { note: secretText("args") },
|
|
101
|
+
status: "failed",
|
|
102
|
+
output: { lines: [secretText("output")] },
|
|
103
|
+
error: scrubSecrets(secretText("error")),
|
|
104
|
+
startedAt: now,
|
|
105
|
+
finishedAt: now,
|
|
106
|
+
});
|
|
107
|
+
const row = await runRow(id);
|
|
108
|
+
expectRedacted(row?.args, "args");
|
|
109
|
+
expectRedacted(row?.output, "output");
|
|
110
|
+
expectRedacted(row?.error, "error");
|
|
111
|
+
// JSON columns still parse for readers.
|
|
112
|
+
expect((JSON.parse(row!.args) as { note: string }).note).toContain(CONTEXT);
|
|
113
|
+
expect((JSON.parse(row!.output!) as { lines: string[] }).lines[0]).toContain(CONTEXT);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("createScriptRun seals args: nothing readable at rest, redacted for callers", async () => {
|
|
117
|
+
const id = crypto.randomUUID();
|
|
118
|
+
const args = { note: secretText("launch") };
|
|
119
|
+
const { run } = await createScriptRun({
|
|
120
|
+
id,
|
|
121
|
+
agentId: AGENT_ID,
|
|
122
|
+
source: "export default () => 1",
|
|
123
|
+
args,
|
|
124
|
+
});
|
|
125
|
+
const stored = (await runRow(id))?.args ?? "";
|
|
126
|
+
expect(isSealedJson(stored)).toBe(true);
|
|
127
|
+
expect(stored.includes(known.value)).toBe(false);
|
|
128
|
+
expect(stored.includes(ghToken)).toBe(false);
|
|
129
|
+
expect(stored.includes(CONTEXT)).toBe(false);
|
|
130
|
+
expectRedacted((run.args as { note: string }).note, "run.args");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("a relaunch after restart executes with the exact sealed args", async () => {
|
|
134
|
+
const id = crypto.randomUUID();
|
|
135
|
+
const args = { note: secretText("relaunch"), n: 3 };
|
|
136
|
+
await createScriptRun({ id, agentId: AGENT_ID, source: "export default () => 1", args });
|
|
137
|
+
await updateScriptRun(id, { status: "running" });
|
|
138
|
+
|
|
139
|
+
// A restarted API sees the run as running with no live process and
|
|
140
|
+
// relaunches it from the row, as reconcileScriptRuns does.
|
|
141
|
+
const [recovered] = await getRunningScriptRuns();
|
|
142
|
+
expect(recovered?.id).toBe(id);
|
|
143
|
+
expectRedacted((recovered!.args as { note: string }).note, "recovered.args");
|
|
144
|
+
|
|
145
|
+
let launched: StartScriptExecutionInput | undefined;
|
|
146
|
+
let exit!: (result: ScriptExecutionResult) => void;
|
|
147
|
+
setScriptRunExecutor({
|
|
148
|
+
async start(input) {
|
|
149
|
+
launched = input;
|
|
150
|
+
return {
|
|
151
|
+
pid: null,
|
|
152
|
+
tmpdir: "/tmp",
|
|
153
|
+
startedAtMs: Date.now(),
|
|
154
|
+
exited: new Promise((resolve) => {
|
|
155
|
+
exit = resolve;
|
|
156
|
+
}),
|
|
157
|
+
async terminate() {},
|
|
158
|
+
async cleanup() {},
|
|
159
|
+
};
|
|
160
|
+
},
|
|
161
|
+
isRunning: () => false,
|
|
162
|
+
async terminatePid() {},
|
|
163
|
+
});
|
|
164
|
+
try {
|
|
165
|
+
await startScriptRunProcess(recovered!, "http://127.0.0.1:1", "test-key");
|
|
166
|
+
expect(launched?.run.args).toEqual(args);
|
|
167
|
+
} finally {
|
|
168
|
+
exit({ exitCode: 0, stderr: "" });
|
|
169
|
+
setScriptRunExecutor(localProcessScriptExecutor);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("updateScriptRun redacts output and error", async () => {
|
|
174
|
+
const id = await seedRun();
|
|
175
|
+
await updateScriptRun(id, {
|
|
176
|
+
status: "failed",
|
|
177
|
+
output: { summary: secretText("output") },
|
|
178
|
+
error: secretText("error"),
|
|
179
|
+
});
|
|
180
|
+
const row = await runRow(id);
|
|
181
|
+
expectRedacted(row?.output, "output");
|
|
182
|
+
expectRedacted(row?.error, "error");
|
|
183
|
+
expect((JSON.parse(row!.output!) as { summary: string }).summary).toContain(CONTEXT);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("updateScriptRunIfRunning redacts the supervisor's stderr error", async () => {
|
|
187
|
+
const id = await seedRun();
|
|
188
|
+
const claimed = await updateScriptRunIfRunning(id, {
|
|
189
|
+
status: "failed",
|
|
190
|
+
pid: null,
|
|
191
|
+
finishedAt: new Date().toISOString(),
|
|
192
|
+
error: `Traceback\n${secretText("stderr")}\n`,
|
|
193
|
+
});
|
|
194
|
+
expect(claimed).toBe(true);
|
|
195
|
+
expectRedacted((await runRow(id))?.error, "error");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("updateScriptRunIfNotTerminal redacts output", async () => {
|
|
199
|
+
const id = await seedRun();
|
|
200
|
+
const claimed = await updateScriptRunIfNotTerminal(id, {
|
|
201
|
+
status: "completed",
|
|
202
|
+
output: secretText("output"),
|
|
203
|
+
});
|
|
204
|
+
expect(claimed).toBe(true);
|
|
205
|
+
const output = (await runRow(id))?.output;
|
|
206
|
+
expectRedacted(output, "output");
|
|
207
|
+
expect(JSON.parse(output!)).toContain(CONTEXT);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("null error and output still clear the columns", async () => {
|
|
211
|
+
const id = await seedRun();
|
|
212
|
+
await updateScriptRun(id, { output: { a: 1 }, error: "boom" });
|
|
213
|
+
await updateScriptRun(id, { output: undefined, error: null });
|
|
214
|
+
const row = await runRow(id);
|
|
215
|
+
expect(row?.output).toBeNull();
|
|
216
|
+
expect(row?.error).toBeNull();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("upsertScriptRunJournalStep redacts config and error, seals result for replay", async () => {
|
|
220
|
+
const id = await seedRun();
|
|
221
|
+
const result = { text: secretText("result") };
|
|
222
|
+
await upsertScriptRunJournalStep({
|
|
223
|
+
runId: id,
|
|
224
|
+
stepKey: "fetch",
|
|
225
|
+
stepType: "raw-llm",
|
|
226
|
+
config: { prompt: secretText("config") },
|
|
227
|
+
status: "failed",
|
|
228
|
+
result,
|
|
229
|
+
error: scrubSecrets(secretText("error")),
|
|
230
|
+
});
|
|
231
|
+
const row = await getDbClient().get<{
|
|
232
|
+
config: string;
|
|
233
|
+
result: string | null;
|
|
234
|
+
error: string | null;
|
|
235
|
+
}>("SELECT config, result, error FROM script_run_journal WHERE runId = ?", [id]);
|
|
236
|
+
expectRedacted(row?.config, "config");
|
|
237
|
+
expectRedacted(row?.error, "error");
|
|
238
|
+
expect((JSON.parse(row!.config) as { prompt: string }).prompt).toContain(CONTEXT);
|
|
239
|
+
// Replay contract: the harness returns this verbatim as the step result,
|
|
240
|
+
// so it is sealed at rest and opened exactly only on the replay route.
|
|
241
|
+
expect(isSealedJson(row?.result ?? "")).toBe(true);
|
|
242
|
+
expect(row!.result!.includes(known.value)).toBe(false);
|
|
243
|
+
expect(row!.result!.includes(ghToken)).toBe(false);
|
|
244
|
+
expect((await getScriptRunJournalStep(id, "fetch"))?.result).toEqual(result);
|
|
245
|
+
const [listed] = await listScriptRunJournalSteps(id);
|
|
246
|
+
expectRedacted((listed?.result as { text: string }).text, "listed result");
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe("scrubJsonValue", () => {
|
|
251
|
+
test("output always parses and keeps non-secret structure", () => {
|
|
252
|
+
known = syntheticSecret("jsonguard");
|
|
253
|
+
refreshSecretScrubberCache();
|
|
254
|
+
try {
|
|
255
|
+
const value = { a: [`k=${known.value}`, 2], b: { c: CONTEXT }, d: null };
|
|
256
|
+
const out = scrubJsonValue(value);
|
|
257
|
+
expect(out.includes(known.value)).toBe(false);
|
|
258
|
+
expect(out).toContain("[REDACTED:");
|
|
259
|
+
expect(JSON.parse(out)).toEqual({
|
|
260
|
+
a: [expect.stringContaining("[REDACTED:"), 2],
|
|
261
|
+
b: { c: CONTEXT },
|
|
262
|
+
d: null,
|
|
263
|
+
});
|
|
264
|
+
} finally {
|
|
265
|
+
known.cleanup();
|
|
266
|
+
refreshSecretScrubberCache();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("breaksJsonValidity flags only a valid-to-invalid change", () => {
|
|
271
|
+
expect(breaksJsonValidity('{"a":"x"}', '{"a":"[REDACTED:x]')).toBe(true);
|
|
272
|
+
expect(breaksJsonValidity('{"a":"x"}', '{"a":"[REDACTED:x]"}')).toBe(false);
|
|
273
|
+
expect(breaksJsonValidity("not json", "still not")).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -6,11 +6,16 @@ import { createApp } from "../apps/store";
|
|
|
6
6
|
import { closeDb, createAgent, getDbClient, initDb } from "../be/db";
|
|
7
7
|
import { getScript, listScripts } from "../be/scripts/db";
|
|
8
8
|
import { setScriptEmbeddingProviderForTests } from "../be/scripts/embeddings";
|
|
9
|
+
import { configSecretName, registerStoredSecret } from "../be/secret-registry";
|
|
9
10
|
import { handleCore } from "../http/core";
|
|
10
11
|
import { handleScripts } from "../http/scripts";
|
|
11
12
|
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
clearVolatileSecretsForTesting,
|
|
15
|
+
refreshSecretScrubberCache,
|
|
16
|
+
} from "../utils/secret-scrubber";
|
|
13
17
|
import { SKIP_SANDBOX_SPAWN_TESTS } from "./sandbox-spawn-test-helpers";
|
|
18
|
+
import { randomToken } from "./synthetic-secret-helpers";
|
|
14
19
|
|
|
15
20
|
const skip = test.skipIf(SKIP_SANDBOX_SPAWN_TESTS);
|
|
16
21
|
|
|
@@ -648,3 +653,79 @@ describe("/api/scripts HTTP", () => {
|
|
|
648
653
|
expect(result?.argsJsonSchema).not.toBeNull();
|
|
649
654
|
});
|
|
650
655
|
});
|
|
656
|
+
|
|
657
|
+
describe("script source with an embedded secret is refused at write", () => {
|
|
658
|
+
const sourceWith = (literal: string) =>
|
|
659
|
+
`export default async () => ({ auth: ${JSON.stringify(literal)} });`;
|
|
660
|
+
|
|
661
|
+
async function storedSourceRows(): Promise<number> {
|
|
662
|
+
const row = await getDbClient().get<{ n: number }>(
|
|
663
|
+
`SELECT (SELECT COUNT(*) FROM scripts) + (SELECT COUNT(*) FROM script_versions)
|
|
664
|
+
+ (SELECT COUNT(*) FROM script_runs) AS n`,
|
|
665
|
+
);
|
|
666
|
+
return row?.n ?? 0;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
type Refusal = { error: string; message: string; findings: { kind: string; id: string }[] };
|
|
670
|
+
|
|
671
|
+
async function expectRefused(
|
|
672
|
+
response: TestResponse,
|
|
673
|
+
secretValue: string,
|
|
674
|
+
finding: { kind: string; id: string },
|
|
675
|
+
): Promise<void> {
|
|
676
|
+
expect(response.status).toBe(400);
|
|
677
|
+
expect(response.text).not.toContain(secretValue);
|
|
678
|
+
const body = (await response.json()) as Refusal;
|
|
679
|
+
expect(body.error).toBe("source_contains_secret");
|
|
680
|
+
expect(body.findings).toContainEqual(finding);
|
|
681
|
+
expect(body.message).toContain(finding.id);
|
|
682
|
+
expect(body.message).toContain("ctx.swarm.config.get");
|
|
683
|
+
expect(body.message).toContain("credential binding");
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
afterAll(() => clearVolatileSecretsForTesting());
|
|
687
|
+
|
|
688
|
+
test("script-upsert refuses a registered secret in every encoded form, and persists nothing", async () => {
|
|
689
|
+
const value = `upsert-${randomToken(32)}`;
|
|
690
|
+
const name = configSecretName("UPSTREAM_TOKEN");
|
|
691
|
+
registerStoredSecret(value, name);
|
|
692
|
+
const before = await storedSourceRows();
|
|
693
|
+
for (const form of [
|
|
694
|
+
value,
|
|
695
|
+
Buffer.from(value).toString("base64"),
|
|
696
|
+
Buffer.from(value).toString("base64url"),
|
|
697
|
+
]) {
|
|
698
|
+
const response = await upsert({ name: "leaky-upsert", source: sourceWith(form) });
|
|
699
|
+
await expectRefused(response, value, { kind: "registered-secret", id: name });
|
|
700
|
+
expect(response.text).not.toContain(form);
|
|
701
|
+
}
|
|
702
|
+
expect(await storedSourceRows()).toBe(before);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
test("script-upsert refuses a gitleaks token shape and names the rule", async () => {
|
|
706
|
+
const token = `ghp_${randomToken(36)}`;
|
|
707
|
+
const before = await storedSourceRows();
|
|
708
|
+
const response = await upsert({ name: "leaky-gitleaks", source: sourceWith(token) });
|
|
709
|
+
await expectRefused(response, token, { kind: "gitleaks-rule", id: "github-pat" });
|
|
710
|
+
expect(await storedSourceRows()).toBe(before);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test("inline script-run refuses before running, saving a scratch script or recording the run", async () => {
|
|
714
|
+
const value = `inline-${randomToken(32)}`;
|
|
715
|
+
const name = configSecretName("INLINE_TOKEN");
|
|
716
|
+
registerStoredSecret(value, name);
|
|
717
|
+
const before = await storedSourceRows();
|
|
718
|
+
const response = await dispatch("/api/scripts/run", {
|
|
719
|
+
method: "POST",
|
|
720
|
+
agentId: workerId,
|
|
721
|
+
body: JSON.stringify({ source: sourceWith(value), intent: "leaky inline" }),
|
|
722
|
+
});
|
|
723
|
+
await expectRefused(response, value, { kind: "registered-secret", id: name });
|
|
724
|
+
expect(await storedSourceRows()).toBe(before);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
test("control: clean source still upserts", async () => {
|
|
728
|
+
const response = await upsert({ name: "clean-upsert", source: validSource(2) });
|
|
729
|
+
expect(response.status).toBe(200);
|
|
730
|
+
});
|
|
731
|
+
});
|