@piagent/platform 1.5.5 → 1.6.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.
Files changed (227) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +9 -8
  3. package/README.vi.md +9 -5
  4. package/adapters/web-frontend/profile.json +2 -4
  5. package/architecture/layers.json +18 -3
  6. package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
  7. package/benchmarks/deep-logic-v1/grade.mjs +272 -10
  8. package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
  9. package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
  10. package/benchmarks/deep-logic-v1/project/package.json +1 -1
  11. package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
  12. package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
  13. package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
  14. package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
  15. package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
  16. package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
  17. package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
  18. package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
  19. package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
  20. package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
  21. package/benchmarks/deep-logic-v1/suite.json +51 -3
  22. package/benchmarks/deep-logic-v1/variant.mjs +22 -1
  23. package/benchmarks/production-v1/spend-control.v1.json +68 -0
  24. package/benchmarks/production-v1/suite.json +42 -3
  25. package/catalog/capabilities.json +6 -6
  26. package/docs/command-reference-vietnamese.md +6 -6
  27. package/docs/context-engine.md +7 -1
  28. package/docs/distribution-standard.md +7 -7
  29. package/docs/en/architecture.md +1 -0
  30. package/docs/languages.json +4 -4
  31. package/docs/model-options.md +8 -5
  32. package/docs/operator-manual-vietnamese.md +13 -11
  33. package/docs/project-adapters.md +2 -2
  34. package/docs/publishing-for-teams.md +5 -0
  35. package/docs/quality-benchmark.md +271 -62
  36. package/docs/release-install-policy.md +10 -9
  37. package/docs/runtime-policy-design.md +4 -4
  38. package/docs/runtime-quality-baseline.md +13 -6
  39. package/docs/security-threat-model.md +2 -2
  40. package/docs/task-lifecycle-vietnamese.md +2 -2
  41. package/docs/team-onboarding.md +2 -2
  42. package/docs/usage-observability.md +7 -2
  43. package/docs/vi/architecture.md +1 -0
  44. package/docs-site/architecture.html +1 -1
  45. package/docs-site/benchmark.html +1 -1
  46. package/docs-site/commands.html +1 -1
  47. package/docs-site/context-engine.html +1 -1
  48. package/docs-site/en/architecture.html +1 -1
  49. package/docs-site/en/benchmark.html +1 -1
  50. package/docs-site/en/commands.html +1 -1
  51. package/docs-site/en/context-engine.html +1 -1
  52. package/docs-site/en/index.html +1 -1
  53. package/docs-site/en/mcp-approval.html +1 -1
  54. package/docs-site/en/mcp-auth.html +1 -1
  55. package/docs-site/en/mcp-servers.html +1 -1
  56. package/docs-site/en/mcp.html +1 -1
  57. package/docs-site/en/permissions.html +1 -1
  58. package/docs-site/en/profiles.html +1 -1
  59. package/docs-site/en/quickstart.html +1 -1
  60. package/docs-site/en/runtime.html +1 -1
  61. package/docs-site/en/scope.html +1 -1
  62. package/docs-site/en/security.html +1 -1
  63. package/docs-site/en/team.html +1 -1
  64. package/docs-site/en/whats-new.html +1 -1
  65. package/docs-site/en/workflows.html +1 -1
  66. package/docs-site/index.html +1 -1
  67. package/docs-site/mcp-approval.html +1 -1
  68. package/docs-site/mcp-auth.html +1 -1
  69. package/docs-site/mcp-servers.html +1 -1
  70. package/docs-site/mcp.html +1 -1
  71. package/docs-site/permissions.html +1 -1
  72. package/docs-site/profiles.html +1 -1
  73. package/docs-site/quickstart.html +1 -1
  74. package/docs-site/runtime.html +1 -1
  75. package/docs-site/scope.html +1 -1
  76. package/docs-site/security.html +1 -1
  77. package/docs-site/team.html +1 -1
  78. package/docs-site/whats-new.html +1 -1
  79. package/docs-site/workflows.html +1 -1
  80. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  81. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  82. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  83. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  84. package/evals/fs4-readiness-matrix.v1.json +2 -2
  85. package/evals/fs5-causal-arm.v1.json +2 -2
  86. package/evals/fs5-pilot-protocol.v1.json +2 -2
  87. package/evals/fs5-pilot-protocol.v4.json +6 -5
  88. package/evals/fs5-pilot-protocol.v5.json +7 -6
  89. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  90. package/package.json +3 -1
  91. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  92. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  93. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  94. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  95. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  96. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  97. package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
  98. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  99. package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -4
  100. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  101. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  102. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  103. package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
  104. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  105. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  106. package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
  107. package/packages/piagent-core/benchmark/benchmark-report.js +116 -12
  108. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  109. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  110. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +782 -0
  111. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  112. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  113. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  114. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  115. package/packages/piagent-core/benchmark/benchmark-suite.js +128 -2
  116. package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
  117. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  118. package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
  119. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  120. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  121. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  122. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  123. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  124. package/packages/piagent-core/extensions/acceptance-receipt.js +34 -11
  125. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  126. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  127. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  128. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  129. package/packages/piagent-core/extensions/context-engine.js +148 -145
  130. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  131. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  132. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  133. package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
  134. package/packages/piagent-core/extensions/criterion-graph.js +78 -8
  135. package/packages/piagent-core/extensions/guard-types.ts +2 -0
  136. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  137. package/packages/piagent-core/extensions/piagent-guard.ts +233 -41
  138. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  139. package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
  140. package/packages/piagent-core/extensions/task-state.js +40 -134
  141. package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
  142. package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
  143. package/packages/piagent-core/package.json +1 -1
  144. package/packages/piagent-core/policies/base-policy.json +2 -0
  145. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  146. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  147. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  148. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  149. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  150. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  151. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  152. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  153. package/packages/piagent-core/runtime/hooks/completion-hook.ts +5 -6
  154. package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
  155. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  156. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
  157. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  158. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
  159. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +4 -2
  160. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  161. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  162. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  163. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +53 -0
  164. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
  165. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  166. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  167. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  168. package/packages/piagent-core/runtime/orchestration/role-policy.ts +1 -1
  169. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  170. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
  171. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
  172. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  173. package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
  174. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  175. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  176. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +55 -2
  177. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  178. package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
  179. package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
  180. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
  181. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
  182. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  183. package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
  184. package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
  185. package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
  186. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  187. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  188. package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
  189. package/packages/piagent-core/security/sensitive-data.js +13 -218
  190. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  191. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  192. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  193. package/packages/piagent-core/security/sensitive-text.js +234 -0
  194. package/packages/piagent-core/subagents/piagent-worker.md +1 -1
  195. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
  196. package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
  197. package/packages/piagent-webui/dist/client/index.html +1 -1
  198. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  199. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  200. package/packages/piagent-webui/gateway/gateway-session-stream.ts +93 -7
  201. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  202. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  203. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  204. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  205. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
  206. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  207. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -124
  208. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  209. package/packages/piagent-webui/server/transcript-projection.ts +22 -4
  210. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  211. package/schemas/piagent-webui/catalog-v1.json +9 -0
  212. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  213. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  214. package/schemas/task-contract.schema.json +12 -1
  215. package/scripts/benchmark-runner-core.mjs +355 -359
  216. package/scripts/benchmark-runner-finalization.mjs +309 -0
  217. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  218. package/scripts/benchmark-runner-support.mjs +200 -0
  219. package/scripts/benchmark-session.mjs +95 -41
  220. package/scripts/configure-model-scope.sh +4 -3
  221. package/scripts/helper-evaluation.mjs +1 -1
  222. package/scripts/install-global.sh +2 -2
  223. package/scripts/pi-usage-history.mjs +37 -10
  224. package/scripts/setup.sh +2 -2
  225. package/scripts/verify-local.sh +5 -1
  226. package/templates/global/settings.json +2 -1
  227. package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
@@ -1,221 +1,16 @@
1
- const REDACTION = "[REDACTED_SECRET]";
2
-
3
- const TOKEN_PATTERNS = [
4
- /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
5
- /\bAKIA[0-9A-Z]{16}\b/g,
6
- /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
7
- /\bgh[opsru]_[A-Za-z0-9_]{20,}\b/g,
8
- /\bsk-(?:(?:proj|svcacct|admin|ant)-)?[A-Za-z0-9_-]{20,}\b/g,
9
- /\bsk_(?:live|test)_[A-Za-z0-9]{16,}\b/g,
10
- /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
11
- // An npm token was recognised only where something named it -- `NPM_TOKEN=...`
12
- // caught the assignment, a field called `token` caught the field -- so the one
13
- // place it actually appears was missed: `//registry.npmjs.org/:_authToken=npm_…`
14
- // is the whole of an `.npmrc`, and a `cat` of that file or a failing publish log
15
- // carried the credential through unredacted. Every other provider here is
16
- // recognised by the shape of its token; npm was the gap.
17
- /\bnpm_[A-Za-z0-9]{30,}\b/g,
18
- /\bAIza[0-9A-Za-z_-]{20,}\b/g,
19
- /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
20
- ];
21
-
22
- const CONNECTION_URL_PATTERN = /\b((?:https?|ftp|postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/"']+:)([^@\s/"']+)(@[^\s"'<>]+)/gi;
23
- const BEARER_PATTERN = /\b(Bearer\s+)([A-Za-z0-9._~+/=-]{20,})\b/gi;
24
- const BASIC_AUTH_PATTERN = /\b(Basic\s+)([A-Za-z0-9+/]{12,}={0,2})(?=$|[\s,;}])/gi;
25
- // The value is one whitespace-free token, not the rest of the line. Taking the
26
- // rest of the line made the result depend on where the header sat among the
27
- // arguments: `curl URL -H "Authorization: Token abc123"` yielded `abc123"`, short
28
- // enough to keep, while moving the header before the URL swept the URL into the
29
- // value and pushed it past the length bar. Same header, same credential, opposite
30
- // outcome. Credentials carry no spaces, so stopping at the first one also keeps
31
- // prose like "Authorization: not required for local runs" intact.
32
- const AUTHORIZATION_HEADER_PATTERN = /\b(Authorization\s*:\s*)([A-Za-z][A-Za-z0-9_-]{0,31})(\s+)([^\s\r\n"'`]+)/gi;
33
-
34
- // A credential passed as a command-line option, which is how most CLIs take one.
35
- // The assignment patterns below require a key starting with a letter, so every
36
- // `--token=...` and `--token ...` walked straight past them.
37
- const CLI_OPTION_SECRET_PATTERN = /(^|[\s({[])(--[A-Za-z][A-Za-z0-9_.-]{0,80})(=|\s+)("[^"\r\n]*"|'[^'\r\n]*'|[^\s"'`;,)}]+)/g;
38
-
39
- // Under one of these schemes the header carries an opaque credential, so its
40
- // length says nothing. Anything else after `Authorization:` is more likely prose
41
- // — "Authorization: not required" — and keeps the ambiguous-syntax length bar.
42
- const AUTHORIZATION_SCHEMES = new Set([
43
- "bearer", "basic", "token", "digest", "negotiate", "ntlm",
44
- "oauth", "hawk", "signature", "apikey", "jwt", "aws4-hmac-sha256"
45
- ]);
46
- const QUERY_SECRET_PATTERN = /([?&])([A-Za-z][A-Za-z0-9_.-]{0,80})(=)([^&#\s]+)/gi;
47
- const DOUBLE_QUOTED_SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?[A-Za-z][A-Za-z0-9_.-]{0,80}(?:"|')?\s*)(:|=(?!=))\s*"((?:\\.|[^"\\\r\n])*)"/gim;
48
- const SINGLE_QUOTED_SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?[A-Za-z][A-Za-z0-9_.-]{0,80}(?:"|')?\s*)(:|=(?!=))\s*'((?:\\.|[^'\\\r\n])*)'/gim;
49
- const SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?[A-Za-z][A-Za-z0-9_.-]{0,80}(?:"|')?\s*)(:|=(?!=))\s*([^"'\s,;}]+)/gim;
50
- const WHITESPACE_SECRET_ASSIGNMENT_PATTERN = /(^|[\s{[,(;])((?:"|')?(?:aws_)?secret_access_key(?:"|')?\s+)(["']?)([A-Za-z0-9/+=]{20,})/gim;
51
-
52
- const SENSITIVE_KEY_TERMS = [
53
- "api_key",
54
- "token",
55
- "password",
56
- "passwd",
57
- "pwd",
58
- "secret",
59
- "credential",
60
- "client_secret",
61
- "access_token",
62
- "refresh_token",
63
- "secret_access_key",
64
- "passphrase",
65
- "private_key",
66
- "signing_key",
67
- "authorization"
68
- ];
69
- const SENSITIVE_PLURAL_KEYS = new Set([
70
- "tokens",
71
- "api_keys",
72
- "passwords",
73
- "secrets",
74
- "credentials",
75
- "client_secrets",
76
- "access_tokens",
77
- "refresh_tokens",
78
- "session_tokens",
79
- "passphrases",
80
- "private_keys",
81
- "signing_keys",
82
- "authorizations"
83
- ]);
84
-
85
- function normalizeSecretValue(value) {
86
- return String(value ?? "")
87
- .trim()
88
- .replace(/^[`"']|[`"']$/g, "")
89
- .replace(/[.;)]+$/g, "");
90
- }
91
-
92
- function patternMatches(pattern, value) {
93
- pattern.lastIndex = 0;
94
- return pattern.test(value);
95
- }
96
-
97
- function looksLikeKnownSecret(value) {
98
- return TOKEN_PATTERNS.some((pattern) => patternMatches(pattern, value))
99
- || patternMatches(CONNECTION_URL_PATTERN, value)
100
- || patternMatches(BEARER_PATTERN, value);
101
- }
102
-
103
- function normalizeSecretKey(key) {
104
- return String(key ?? "")
105
- .trim()
106
- .replace(/^["']|["']$/g, "")
107
- .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
108
- .replace(/[^A-Za-z0-9]+/g, "_")
109
- .replace(/^_+|_+$/g, "")
110
- .toLowerCase();
111
- }
112
-
113
- function keyLooksSensitive(key) {
114
- const normalized = normalizeSecretKey(key);
115
- return SENSITIVE_PLURAL_KEYS.has(normalized) || SENSITIVE_KEY_TERMS.some((term) => normalized === term
116
- || normalized.endsWith(`_${term}`));
117
- }
118
-
119
- function valueLooksPlaceholder(value) {
120
- const clean = normalizeSecretValue(value);
121
- if (!clean) return true;
122
- if (/^(?:null|undefined|none|false|true|0|""|'')$/i.test(clean)) return true;
123
- if (/^<[^>\n]{1,80}>$/.test(clean)) return true;
124
- if (/^\[REDACTED_SECRET\]$/i.test(clean)) return true;
125
- if (/^(?:not-set|unset|placeholder|example|changeme|change-me|redacted|xxx|\*{3,})$/i.test(clean)) return true;
126
- if (/^your[-_][a-z0-9][a-z0-9_-]*$/i.test(clean)) return true;
127
- return false;
128
- }
129
-
130
- // Length is only evidence where the syntax is ambiguous, and `=` is not.
131
- //
132
- // A short value used to be kept in the clear whatever the syntax, so `TOKEN=hunter2`
133
- // and `PASSWORD=short7` went into the ledger verbatim. Every name in
134
- // SENSITIVE_KEY_TERMS states outright what it holds — there is no `key` or `id`
135
- // among them — so under an `=`, or inside quotes, a non-placeholder value is the
136
- // secret the key says it is, at any length.
137
- //
138
- // A bare `:` is the ambiguous case, because English uses it too: `password: short`
139
- // and `The secret: always run tests` are prose, and `Authorization: Token abc123`
140
- // puts the scheme name where a value would go. Those keep a length bar.
141
- const SHORT_VALUE_MIN_LENGTH = 8;
142
- const AMBIGUOUS_VALUE_MIN_LENGTH = 12;
143
-
144
- function valueLooksSensitive(key, value, syntax = "unquoted-colon") {
145
- const clean = normalizeSecretValue(value);
146
- if (!clean || valueLooksPlaceholder(clean)) return false;
147
- if (looksLikeKnownSecret(clean)) return true;
148
- if (!keyLooksSensitive(key)) return false;
149
- if (syntax !== "unquoted-colon") return true;
150
- return clean.length >= (keyNamesASecretOutright(key) ? SHORT_VALUE_MIN_LENGTH : AMBIGUOUS_VALUE_MIN_LENGTH);
151
- }
152
-
153
- // Under a bare `:`, these names carry enough intent on their own to redact a short
154
- // value; the broader terms wait for length before touching prose.
155
- function keyNamesASecretOutright(key) {
156
- const normalized = normalizeSecretKey(key);
157
- return ["password", "passwd", "pwd", "secret", "credential", "passphrase", "private_key", "signing_key"]
158
- .some((term) => normalized === term || normalized.endsWith(`_${term}`))
159
- || ["passwords", "secrets", "credentials", "passphrases", "private_keys", "signing_keys"].includes(normalized);
160
- }
161
-
162
- function assignmentSyntax(separator) {
163
- return separator === "=" ? "assignment" : "unquoted-colon";
164
- }
165
-
166
- export function redactSensitiveText(input) {
167
- if (typeof input !== "string") return { text: "", redacted: false };
168
- let text = input;
169
-
170
- text = text.replace(CONNECTION_URL_PATTERN, (_match, prefix, _password, suffix) => `${prefix}${REDACTION}${suffix}`);
171
- text = text.replace(BEARER_PATTERN, (_match, prefix) => `${prefix}${REDACTION}`);
172
- text = text.replace(BASIC_AUTH_PATTERN, (_match, prefix) => `${prefix}${REDACTION}`);
173
- text = text.replace(AUTHORIZATION_HEADER_PATTERN, (match, prefix, scheme, spacing, value) => {
174
- const syntax = AUTHORIZATION_SCHEMES.has(scheme.toLowerCase()) ? "assignment" : "unquoted-colon";
175
- if (!valueLooksSensitive("authorization", value, syntax)) return match;
176
- return `${prefix}${scheme}${spacing}${REDACTION}`;
177
- });
178
-
179
- // Over-redaction is the safe direction here: a flag whose name says "secret"
180
- // and whose argument is a plain name — `docker service create --secret build` —
181
- // loses that name from the record and cannot serve as verify evidence. Reading
182
- // the name as the credential it is called is the cheaper mistake.
183
- text = text.replace(CLI_OPTION_SECRET_PATTERN, (match, prefix, flag, separator, value) => {
184
- // A following option is the next flag, not this flag's argument.
185
- if (separator !== "=" && value.startsWith("-")) return match;
186
- if (!keyLooksSensitive(flag) || !valueLooksSensitive(flag, value, "assignment")) return match;
187
- return `${prefix}${flag}${separator === "=" ? "= " : " "}${REDACTION}`;
188
- });
189
-
190
- for (const pattern of TOKEN_PATTERNS) text = text.replace(pattern, REDACTION);
191
-
192
- text = text.replace(QUERY_SECRET_PATTERN, (match, prefix, key, separator, value) => {
193
- if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, "assignment")) return match;
194
- return `${prefix}${key}${separator}${REDACTION}`;
195
- });
196
-
197
- text = text.replace(DOUBLE_QUOTED_SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, separator, value) => {
198
- if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, "quoted")) return match;
199
- return `${prefix}${key}${separator} "${REDACTION}"`;
200
- });
201
-
202
- text = text.replace(SINGLE_QUOTED_SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, separator, value) => {
203
- if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, "quoted")) return match;
204
- return `${prefix}${key}${separator} '${REDACTION}'`;
205
- });
206
-
207
- text = text.replace(SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, separator, value) => {
208
- if (!keyLooksSensitive(key) || !valueLooksSensitive(key, value, assignmentSyntax(separator))) return match;
209
- return `${prefix}${key}${separator} ${REDACTION}`;
210
- });
211
-
212
- text = text.replace(WHITESPACE_SECRET_ASSIGNMENT_PATTERN, (match, prefix, key, quote, value) => {
213
- if (!valueLooksSensitive(key, value)) return match;
214
- return `${prefix}${key}${quote}${REDACTION}`;
215
- });
216
-
217
- return { text, redacted: text !== input };
218
- }
1
+ import {
2
+ REDACTION,
3
+ keyLooksSensitive,
4
+ redactSensitiveText,
5
+ valueLooksSensitive
6
+ } from "./sensitive-text.js";
7
+
8
+ export { redactSensitiveText } from "./sensitive-text.js";
9
+ export {
10
+ redactSensitiveShellSourceText,
11
+ redactSensitiveSourceText
12
+ } from "./sensitive-source-formats.js";
13
+ export { redactSensitiveProjectFileText } from "./sensitive-project-file.js";
219
14
 
220
15
  export function containsSensitiveText(input) {
221
16
  return typeof input === "string" && redactSensitiveText(input).redacted;
@@ -0,0 +1,42 @@
1
+ import { redactSensitiveText } from "./sensitive-text.js";
2
+ import {
3
+ redactSensitiveCssText,
4
+ redactSensitiveMarkupText,
5
+ redactSensitiveObjectiveCText,
6
+ redactSensitiveShellSourceText,
7
+ redactSensitiveSourceText,
8
+ redactSensitiveYamlText,
9
+ redactSourceText
10
+ } from "./sensitive-source-formats.js";
11
+
12
+ const PROJECT_SOURCE_CODE_PATH = /\.(?:[cm]?[jt]sx?|py|rb|php|java|kt|kts|swift|go|rs|c|cc|cpp|cxx|h|hh|hpp|hxx|cs|scala|lua|r|dart|ex|exs|erl|hrl|fs|fsx|vb|sql|vue|svelte)$/i;
13
+ const PROJECT_SHELL_SOURCE_PATH = /\.(?:sh|bash|zsh|fish)$/i;
14
+ const PROJECT_HASH_COMMENT_SOURCE_PATH = /\.(?:py|rb|php|r|ex|exs)$/i;
15
+ const PROJECT_YAML_PATH = /\.ya?ml$/i;
16
+ const PROJECT_MARKUP_PATH = /\.(?:html?|xml|svg)$/i;
17
+ const PROJECT_CSS_PATH = /\.(?:css|scss)$/i;
18
+ const PROJECT_OBJECTIVE_C_PATH = /\.mm?$/i;
19
+
20
+ export function redactSensitiveProjectFileText(filePath, input) {
21
+ const source = typeof input === "string" ? input : "";
22
+ const result = PROJECT_SHELL_SOURCE_PATH.test(String(filePath ?? ""))
23
+ ? redactSensitiveShellSourceText(source)
24
+ : PROJECT_YAML_PATH.test(String(filePath ?? ""))
25
+ ? redactSensitiveYamlText(source)
26
+ : PROJECT_MARKUP_PATH.test(String(filePath ?? ""))
27
+ ? redactSensitiveMarkupText(source)
28
+ : PROJECT_CSS_PATH.test(String(filePath ?? ""))
29
+ ? redactSensitiveCssText(source)
30
+ : PROJECT_OBJECTIVE_C_PATH.test(String(filePath ?? ""))
31
+ ? redactSensitiveObjectiveCText(source)
32
+ : PROJECT_SOURCE_CODE_PATH.test(String(filePath ?? ""))
33
+ ? redactSourceText(source, {
34
+ shellOnly: false,
35
+ hashComments: PROJECT_HASH_COMMENT_SOURCE_PATH.test(String(filePath ?? ""))
36
+ })
37
+ : redactSensitiveText(source);
38
+ return {
39
+ ...result,
40
+ lineCountPreserved: (source.match(/\n/g) ?? []).length === (result.text.match(/\n/g) ?? []).length
41
+ };
42
+ }
@@ -0,0 +1,330 @@
1
+ import {
2
+ REDACTION,
3
+ SENSITIVE_KEY_TERMS,
4
+ SENSITIVE_PLURAL_KEYS,
5
+ keyLooksSensitive,
6
+ looksLikeKnownSecret,
7
+ normalizeSecretKey,
8
+ redactSensitiveTextInternal,
9
+ valueLooksPlaceholder
10
+ } from "./sensitive-text.js";
11
+
12
+ const SOURCE_ASSIGNMENT_START_PATTERN = /(^|[\s{[(;,])((?:(?:const|let|var)\s+)?(#?[$@%]?[A-Za-z_$][A-Za-z0-9_$]*)\s*(?:>>>=|<<=|>>=|\|\|=|\?\?=|&&=|\+=|-=|\*=|\/=|%=|&=|\|=|\^=|<-|:=|=)\s*)/g;
13
+ const SOURCE_TYPED_DECLARATION_START_PATTERN = /(^|[\s{[(;,])((?:const|var)\s+(#?[$@%]?[A-Za-z_$][A-Za-z0-9_$]*)\s+(?:\*|\[\])*[A-Za-z_$][A-Za-z0-9_$.[\]*]*\s*=\s*)/g;
14
+ const SOURCE_COMPOUND_ASSIGNMENT_START_PATTERN = /(^|[\s{[(;,])((#?[$@%]?[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\?\.|\.|->|::)#?[A-Za-z_$][A-Za-z0-9_$]*|\[(?:"[^"\r\n]*"|'[^'\r\n]*'|[A-Za-z_$][A-Za-z0-9_$]*)\])+\s*(?:>>>=|<<=|>>=|\|\|=|\?\?=|&&=|\+=|-=|\*=|\/=|%=|&=|\|=|\^=|<-|:=|=)\s*))/g;
15
+ const SOURCE_BRACE_INITIALIZER_START_PATTERN = /(^|[\s{[(;,])((?:(?:const\s+)?[A-Za-z_$][A-Za-z0-9_$:<>,*&[\]]*\s+)+(#?[$@%]?[A-Za-z_$][A-Za-z0-9_$]*)\s*\{\s*)/g;
16
+ const SOURCE_PREPROCESSOR_DEFINE_START_PATTERN = /(^|[\r\n])([ \t]*#\s*define\s+([A-Za-z_][A-Za-z0-9_]*)\s+)/g;
17
+ const SOURCE_PROPERTY_START_PATTERN = /(^|[\s{[(;,])((?:["']?)([A-Za-z_$][A-Za-z0-9_$]*)(?:["']?)\s*:(?!=)\s*)/g;
18
+ const SOURCE_COMPUTED_PROPERTY_START_PATTERN = /(^|[\s{[(;,])(\[\s*["']([A-Za-z_$][A-Za-z0-9_$]*)["']\s*\]\s*:(?!=)\s*)/g;
19
+ const SOURCE_TEMPLATE_DOUBLE_BINDING_PATTERN = /((?::|v-bind:)([A-Za-z_$][A-Za-z0-9_$.-]*)\s*=\s*)"((?:\\.|[^"\\\r\n])*)"/g;
20
+ const SOURCE_TEMPLATE_SINGLE_BINDING_PATTERN = /((?::|v-bind:)([A-Za-z_$][A-Za-z0-9_$.-]*)\s*=\s*)'((?:\\.|[^'\\\r\n])*)'/g;
21
+ const SHELL_REFERENCE_PATTERN = /^\$(?:[A-Za-z_][A-Za-z0-9_]*|\{[A-Za-z_][A-Za-z0-9_]*\}|\([A-Za-z0-9_./:-]+(?:\s+\$[A-Za-z_][A-Za-z0-9_]*)*\))$/;
22
+ const SOURCE_CALL_START_PATTERN = /(^|[^A-Za-z0-9_$])([$@%]?[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\.|->|::)[A-Za-z_$][A-Za-z0-9_$]*)*)\s*\(/g;
23
+
24
+ function balancedSourceExpression(value) {
25
+ const stack = [];
26
+ const pairs = { ")": "(", "]": "[", "}": "{" };
27
+ for (const character of value) {
28
+ if (character === "(" || character === "[" || character === "{") stack.push(character);
29
+ else if (character === ")" || character === "]" || character === "}") {
30
+ if (stack.pop() !== pairs[character]) return false;
31
+ }
32
+ }
33
+ return stack.length === 0;
34
+ }
35
+
36
+ function maskSourceLookupStrings(value) {
37
+ let valid = true;
38
+ const text = value.replace(/"((?:\\.|[^"\\\r\n])*)"|'((?:\\.|[^'\\\r\n])*)'/g, (match, doubleQuoted, singleQuoted, offset) => {
39
+ const literal = doubleQuoted ?? singleQuoted ?? "";
40
+ const normalized = normalizeSecretKey(literal);
41
+ const exactLookupKey = SENSITIVE_KEY_TERMS.includes(normalized) || SENSITIVE_PLURAL_KEYS.has(normalized);
42
+ const environmentLookupKey = /^[A-Z][A-Z0-9_]{1,79}$/.test(literal) && keyLooksSensitive(literal);
43
+ const before = value.slice(0, offset).trimEnd();
44
+ const after = value.slice(offset + match.length).trimStart();
45
+ const bracketLookup = /\[@?$/.test(before) && after.startsWith("]");
46
+ const accessorLookup = /(?:(?:\.|->|::)(?:get|lookup|valueForKey)|\b(?:getenv|lookup))\s*\($/i.test(before)
47
+ && /^(?:,|\))/.test(after);
48
+ const objectiveAccessorLookup = /(?:objectForKey|valueForKey|forKey)\s*:\s*@?$/i.test(before)
49
+ && /^(?:\]|\s)/.test(after);
50
+ if ((!exactLookupKey && !environmentLookupKey) || (!bracketLookup && !accessorLookup && !objectiveAccessorLookup)) valid = false;
51
+ return "PIAGENT_LOOKUP_KEY";
52
+ });
53
+ if (!valid || /["'`]/.test(text)) return undefined;
54
+ return text;
55
+ }
56
+
57
+ function hasStandaloneNumericSourceLiteral(value) {
58
+ const pattern = /(^|[^A-Za-z0-9_$])(?:0[xX][0-9A-Fa-f]+|0[bB][01]+|0[oO][0-7]+|(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?n?)(?=$|[^A-Za-z0-9_$])/g;
59
+ for (let match = pattern.exec(value); match; match = pattern.exec(value)) {
60
+ const literalStart = match.index + match[1].length;
61
+ const literalEnd = pattern.lastIndex;
62
+ const before = value.slice(0, literalStart).trimEnd();
63
+ const after = value.slice(literalEnd).trimStart();
64
+ const bracketIndex = before.endsWith("[") && after.startsWith("]")
65
+ && /[A-Za-z0-9_$)\]]\s*\[$/.test(before);
66
+ if (!bracketIndex) return true;
67
+ }
68
+ return false;
69
+ }
70
+
71
+ function hasStandaloneKeywordSourceLiteral(value) {
72
+ const pattern = /(^|[^A-Za-z0-9_$])(?:null|undefined|true|false|nil|none|yes|no)(?=$|[^A-Za-z0-9_$])/gi;
73
+ for (let match = pattern.exec(value); match; match = pattern.exec(value)) {
74
+ const literalStart = match.index + match[1].length;
75
+ const before = value.slice(0, literalStart).trimEnd();
76
+ if (!before.endsWith(".") && !before.endsWith("->") && !before.endsWith("::")) return true;
77
+ }
78
+ return false;
79
+ }
80
+
81
+ function sourceReference(value, shellOnly) {
82
+ const clean = String(value ?? "").trim();
83
+ if (!clean) return false;
84
+ if (shellOnly) return !redactSensitiveTextInternal(clean).redacted && SHELL_REFERENCE_PATTERN.test(clean);
85
+ if (looksLikeKnownSecret(clean)) return false;
86
+ // Ruby percent literals and heredoc openers are literal syntax, not dynamic
87
+ // references. Never shield their contents from the source redactor.
88
+ if (/^%(?:q|Q|w|W|i|I|x|r|s)?[({[<]/.test(clean) || /^<<[-~]?/.test(clean)) return false;
89
+ const masked = maskSourceLookupStrings(clean);
90
+ if (!masked || !/^(?:await\s+)?[$@%]?[A-Za-z_]/.test(masked)
91
+ || !/^[A-Za-z0-9_$@%?.()[\]{},!:\s|&+*/<>=-]+$/.test(masked)
92
+ || !balancedSourceExpression(masked)
93
+ || hasStandaloneNumericSourceLiteral(masked)
94
+ || hasStandaloneKeywordSourceLiteral(masked)) return false;
95
+ return true;
96
+ }
97
+
98
+ function sourceExpressionEnd(segment, start) {
99
+ const stack = [];
100
+ const pairs = { ")": "(", "]": "[", "}": "{" };
101
+ let quote = "", escaped = false, sawValue = false;
102
+ for (let index = start; index < segment.length; index += 1) {
103
+ const character = segment[index];
104
+ if (quote) {
105
+ if (escaped) escaped = false;
106
+ else if (character === "\\") escaped = true;
107
+ else if (character === quote) quote = "";
108
+ continue;
109
+ }
110
+ if (character === "\"" || character === "'" || character === "`") {
111
+ quote = character;
112
+ sawValue = true;
113
+ continue;
114
+ }
115
+ if (character === "(" || character === "[" || character === "{") stack.push(character);
116
+ else if (character === ")" || character === "]" || character === "}") {
117
+ if (stack.length === 0) return index;
118
+ if (stack.at(-1) === pairs[character]) stack.pop();
119
+ } else if (stack.length === 0 && (character === ";" || character === ",")) {
120
+ return index;
121
+ } else if (stack.length === 0 && (character === "\r" || character === "\n")) {
122
+ if (character === "\r" && segment[index + 1] === "\n") continue;
123
+ const before = segment.slice(start, index).trimEnd();
124
+ const after = segment.slice(index + 1).trimStart();
125
+ const trailingContinuation = /(?:\b(?:await|return|yield|throw|new)|\?\.|\?\?|\|\||&&|[.?:+*/%&|=,<>{[(\\-])$/.test(before);
126
+ const leadingContinuation = /^(?:\?\.|\?\?|\|\||&&|[([.'"`?:+*/%&|,<>=-])/.test(after);
127
+ if (sawValue && !trailingContinuation && !leadingContinuation) return index;
128
+ } else if (!/\s/.test(character)) {
129
+ sawValue = true;
130
+ }
131
+ }
132
+ return segment.length;
133
+ }
134
+
135
+ function rewriteSensitiveStarts(segment, pattern, { allowDynamic, shellOnly, placeholder, acceptMatch, reference = sourceReference }) {
136
+ let output = "", cursor = 0;
137
+ pattern.lastIndex = 0;
138
+ for (let match = pattern.exec(segment); match; match = pattern.exec(segment)) {
139
+ if (match.index < cursor || !keyLooksSensitive(match[3]) || (acceptMatch && !acceptMatch(match))) continue;
140
+ const valueStart = pattern.lastIndex;
141
+ const valueEnd = sourceExpressionEnd(segment, valueStart);
142
+ const rawExpression = segment.slice(valueStart, valueEnd);
143
+ const expression = rawExpression.trim();
144
+ const complete = segment.slice(match.index, valueEnd);
145
+ output += segment.slice(cursor, match.index);
146
+ const trailingHorizontalWhitespace = rawExpression.match(/[ \t]+$/)?.[0] ?? "";
147
+ const lineEndings = (rawExpression.match(/\r\n|\n|\r/g) ?? []).join("");
148
+ output += allowDynamic && reference(expression, shellOnly)
149
+ ? placeholder(complete)
150
+ : `${match[1]}${match[2]}${REDACTION}${lineEndings}${trailingHorizontalWhitespace}`;
151
+ cursor = valueEnd;
152
+ pattern.lastIndex = valueEnd;
153
+ }
154
+ return `${output}${segment.slice(cursor)}`;
155
+ }
156
+
157
+ function callCloseIndex(segment, openIndex) {
158
+ let depth = 0, quote = "", escaped = false;
159
+ for (let index = openIndex; index < segment.length; index += 1) {
160
+ const character = segment[index];
161
+ if (quote) {
162
+ if (escaped) escaped = false;
163
+ else if (character === "\\") escaped = true;
164
+ else if (character === quote) quote = "";
165
+ continue;
166
+ }
167
+ if (character === "\"" || character === "'" || character === "`") quote = character;
168
+ else if (character === "(") depth += 1;
169
+ else if (character === ")") {
170
+ depth -= 1;
171
+ if (depth === 0) return index;
172
+ }
173
+ }
174
+ return -1;
175
+ }
176
+
177
+ function callArgumentRanges(segment, start, end) {
178
+ const ranges = [];
179
+ let cursor = start, quote = "", escaped = false;
180
+ const stack = [];
181
+ const pairs = { ")": "(", "]": "[", "}": "{" };
182
+ for (let index = start; index <= end; index += 1) {
183
+ const character = index === end ? "," : segment[index];
184
+ if (quote) {
185
+ if (escaped) escaped = false;
186
+ else if (character === "\\") escaped = true;
187
+ else if (character === quote) quote = "";
188
+ continue;
189
+ }
190
+ if (character === "\"" || character === "'" || character === "`") quote = character;
191
+ else if (character === "(" || character === "[" || character === "{") stack.push(character);
192
+ else if (character === ")" || character === "]" || character === "}") {
193
+ if (stack.at(-1) === pairs[character]) stack.pop();
194
+ } else if (character === "," && stack.length === 0) {
195
+ ranges.push({ start: cursor, end: index });
196
+ cursor = index + 1;
197
+ }
198
+ }
199
+ return ranges;
200
+ }
201
+
202
+ function quotedSensitiveLookupKey(value) {
203
+ const match = /^\s*(["'])((?:\\.|(?!\1)[\s\S])*)\1\s*$/.exec(value);
204
+ if (!match) return false;
205
+ const normalized = normalizeSecretKey(match[2]);
206
+ return SENSITIVE_KEY_TERMS.includes(normalized) || SENSITIVE_PLURAL_KEYS.has(normalized)
207
+ || (/^[A-Z][A-Z0-9_]{1,79}$/.test(match[2]) && keyLooksSensitive(match[2]));
208
+ }
209
+
210
+ function setterValueArgument(callee, argumentValues) {
211
+ const name = callee.split(/\.|->|::/).at(-1) ?? "";
212
+ const direct = /^(?:set|put|store|update|write|add|assign)(.+)$/i.exec(name);
213
+ if (direct && keyLooksSensitive(direct[1]) && argumentValues.length >= 1) return 0;
214
+ if (["set", "put", "store", "update", "write", "add", "assign"].includes(name.toLowerCase())
215
+ && argumentValues.length >= 2 && quotedSensitiveLookupKey(argumentValues[0])) return 1;
216
+ return -1;
217
+ }
218
+
219
+ function couldSetSensitiveValue(callee, segment, openIndex) {
220
+ const name = callee.split(/\.|->|::/).at(-1) ?? "";
221
+ const direct = /^(?:set|put|store|update|write|add|assign)(.+)$/i.exec(name);
222
+ if (direct && keyLooksSensitive(direct[1])) return true;
223
+ if (!["set", "put", "store", "update", "write", "add", "assign"].includes(name.toLowerCase())) return false;
224
+
225
+ // Generic setters can only name a sensitive destination through their first
226
+ // quoted argument. Check that argument before looking for the matching close.
227
+ // This keeps deeply nested non-sensitive `set(...)` calls from each rescanning
228
+ // the entire suffix of the source file.
229
+ const firstArgument = /^\s*((?:"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'))\s*,/.exec(segment.slice(openIndex + 1));
230
+ return Boolean(firstArgument && quotedSensitiveLookupKey(firstArgument[1]));
231
+ }
232
+
233
+ function rewriteSensitiveCalls(segment, { allowDynamic, placeholder }) {
234
+ let output = "", cursor = 0;
235
+ SOURCE_CALL_START_PATTERN.lastIndex = 0;
236
+ for (let match = SOURCE_CALL_START_PATTERN.exec(segment); match; match = SOURCE_CALL_START_PATTERN.exec(segment)) {
237
+ if (match.index < cursor) continue;
238
+ const openIndex = SOURCE_CALL_START_PATTERN.lastIndex - 1;
239
+ if (!couldSetSensitiveValue(match[2], segment, openIndex)) {
240
+ // Resume on the opening delimiter so a nested setter in the first
241
+ // argument remains discoverable. Avoiding a full matching-close scan for
242
+ // every ordinary nested call keeps this pass linear for adversarially
243
+ // deep call expressions.
244
+ SOURCE_CALL_START_PATTERN.lastIndex = openIndex;
245
+ continue;
246
+ }
247
+ const closeIndex = callCloseIndex(segment, openIndex);
248
+ if (closeIndex < 0) continue;
249
+ const ranges = callArgumentRanges(segment, openIndex + 1, closeIndex);
250
+ const values = ranges.map((range) => segment.slice(range.start, range.end));
251
+ const valueIndex = setterValueArgument(match[2], values);
252
+ if (valueIndex < 0) {
253
+ // Restart on the opening delimiter so the regex can consume it as the
254
+ // prefix of a nested call in the first argument. Advancing past it skips
255
+ // `setToken(...)` in `wrapper(setToken(...), ...)`.
256
+ SOURCE_CALL_START_PATTERN.lastIndex = openIndex;
257
+ continue;
258
+ }
259
+ const range = ranges[valueIndex];
260
+ const expression = segment.slice(range.start, range.end);
261
+ if (allowDynamic && sourceReference(expression, false)) {
262
+ SOURCE_CALL_START_PATTERN.lastIndex = closeIndex + 1;
263
+ continue;
264
+ }
265
+ output += segment.slice(cursor, range.start);
266
+ output += `${REDACTION}${"\n".repeat((expression.match(/\n/g) ?? []).length)}`;
267
+ cursor = range.end;
268
+ SOURCE_CALL_START_PATTERN.lastIndex = closeIndex + 1;
269
+ }
270
+ return `${output}${segment.slice(cursor)}`;
271
+ }
272
+
273
+ function rewriteSensitiveValueDecorators(segment) {
274
+ const rewrite = (pattern, quote) => segment.replace(pattern, (match, prefix, literal, suffix) => {
275
+ const identifiers = suffix.match(/[$@%]?[A-Za-z_$][A-Za-z0-9_$]*/g) ?? [];
276
+ const field = identifiers.at(-1) ?? "";
277
+ const lookup = /^\$\{[A-Za-z_][A-Za-z0-9_.:-]*\}$/.test(literal);
278
+ if (!keyLooksSensitive(field) || lookup || valueLooksPlaceholder(literal)) return match;
279
+ return `${prefix}${quote}${REDACTION}${quote}${suffix}`;
280
+ });
281
+ let result = rewrite(/(@Value\s*\(\s*)"((?:\\.|[^"\\\r\n])*)"(\s*\)[^;\r\n]{0,160})/g, "\"");
282
+ result = result.replace(/(@Value\s*\(\s*)'((?:\\.|[^'\\\r\n])*)'(\s*\)[^;\r\n]{0,160})/g, (match, prefix, literal, suffix) => {
283
+ const identifiers = suffix.match(/[$@%]?[A-Za-z_$][A-Za-z0-9_$]*/g) ?? [];
284
+ const field = identifiers.at(-1) ?? "";
285
+ const lookup = /^\$\{[A-Za-z_][A-Za-z0-9_.:-]*\}$/.test(literal);
286
+ if (!keyLooksSensitive(field) || lookup || valueLooksPlaceholder(literal)) return match;
287
+ return `${prefix}'${REDACTION}'${suffix}`;
288
+ });
289
+ return result;
290
+ }
291
+
292
+ function rewriteSensitiveSegment(segment, { allowDynamic, shellOnly, placeholder }) {
293
+ let result = rewriteSensitiveStarts(segment, SOURCE_PREPROCESSOR_DEFINE_START_PATTERN, { allowDynamic, shellOnly, placeholder });
294
+ result = rewriteSensitiveStarts(result, SOURCE_COMPOUND_ASSIGNMENT_START_PATTERN, { allowDynamic, shellOnly, placeholder });
295
+ result = rewriteSensitiveStarts(result, SOURCE_TYPED_DECLARATION_START_PATTERN, { allowDynamic, shellOnly, placeholder });
296
+ result = rewriteSensitiveStarts(result, SOURCE_ASSIGNMENT_START_PATTERN, { allowDynamic, shellOnly, placeholder });
297
+ if (!shellOnly) {
298
+ result = rewriteSensitiveStarts(result, SOURCE_BRACE_INITIALIZER_START_PATTERN, {
299
+ allowDynamic,
300
+ shellOnly: false,
301
+ placeholder,
302
+ acceptMatch: (match) => !/^\s*(?:(?:export|public|private|protected|abstract|final|sealed|static|partial)\s+)*(?:class|struct|interface|enum|union|namespace|record)\b/.test(match[2])
303
+ });
304
+ result = rewriteSensitiveStarts(result, SOURCE_COMPUTED_PROPERTY_START_PATTERN, { allowDynamic, shellOnly: false, placeholder });
305
+ result = rewriteSensitiveStarts(result, SOURCE_PROPERTY_START_PATTERN, { allowDynamic, shellOnly: false, placeholder });
306
+ result = rewriteSensitiveCalls(result, { allowDynamic, placeholder });
307
+ result = rewriteSensitiveValueDecorators(result);
308
+ const rewriteTemplate = (pattern, quote) => {
309
+ result = result.replace(pattern, (match, prefix, key, value) => {
310
+ if (!keyLooksSensitive(key)) return match;
311
+ return allowDynamic && sourceReference(value, false)
312
+ ? placeholder(match)
313
+ : `${prefix}${quote}${REDACTION}${quote}`;
314
+ });
315
+ };
316
+ rewriteTemplate(SOURCE_TEMPLATE_DOUBLE_BINDING_PATTERN, "\"");
317
+ rewriteTemplate(SOURCE_TEMPLATE_SINGLE_BINDING_PATTERN, "'");
318
+ }
319
+ return result;
320
+ }
321
+
322
+ export {
323
+ balancedSourceExpression,
324
+ hasStandaloneKeywordSourceLiteral,
325
+ hasStandaloneNumericSourceLiteral,
326
+ maskSourceLookupStrings,
327
+ rewriteSensitiveSegment,
328
+ rewriteSensitiveStarts,
329
+ sourceReference
330
+ };