clinkx 0.3.0 → 1.0.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/CHANGELOG.md +33 -0
- package/LICENSE +10 -0
- package/README.md +82 -26
- package/clinkx-workflows/dist/allowed-roots.d.ts +1 -0
- package/clinkx-workflows/dist/allowed-roots.js +5 -0
- package/clinkx-workflows/dist/allowed-roots.js.map +1 -0
- package/clinkx-workflows/dist/artifacts.d.ts +20 -1
- package/clinkx-workflows/dist/artifacts.js +38 -9
- package/clinkx-workflows/dist/artifacts.js.map +1 -1
- package/clinkx-workflows/dist/backend.d.ts +4 -0
- package/clinkx-workflows/dist/citation-check.d.ts +40 -0
- package/clinkx-workflows/dist/citation-check.js +348 -0
- package/clinkx-workflows/dist/citation-check.js.map +1 -0
- package/clinkx-workflows/dist/clink-client.d.ts +2 -1
- package/clinkx-workflows/dist/clink-client.js +17 -4
- package/clinkx-workflows/dist/clink-client.js.map +1 -1
- package/clinkx-workflows/dist/config.d.ts +145 -16
- package/clinkx-workflows/dist/config.js +384 -26
- package/clinkx-workflows/dist/config.js.map +1 -1
- package/clinkx-workflows/dist/contained-file.d.ts +172 -0
- package/clinkx-workflows/dist/contained-file.js +483 -0
- package/clinkx-workflows/dist/contained-file.js.map +1 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-contract.d.ts +416 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-contract.js +328 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-contract.js.map +1 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-router.d.ts +24 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-router.js +39 -0
- package/clinkx-workflows/dist/context-builder-lifecycle-router.js.map +1 -0
- package/clinkx-workflows/dist/context-cache-key.d.ts +53 -0
- package/clinkx-workflows/dist/context-cache-key.js +91 -0
- package/clinkx-workflows/dist/context-cache-key.js.map +1 -0
- package/clinkx-workflows/dist/context-mode-resolver.d.ts +27 -0
- package/clinkx-workflows/dist/context-mode-resolver.js +126 -0
- package/clinkx-workflows/dist/context-mode-resolver.js.map +1 -0
- package/clinkx-workflows/dist/context-preflight.d.ts +184 -0
- package/clinkx-workflows/dist/context-preflight.js +91 -0
- package/clinkx-workflows/dist/context-preflight.js.map +1 -0
- package/clinkx-workflows/dist/council-expander.d.ts +1 -1
- package/clinkx-workflows/dist/council-expander.js +2 -1
- package/clinkx-workflows/dist/council-expander.js.map +1 -1
- package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +165 -0
- package/clinkx-workflows/dist/dashboard/activity-contract.js +105 -0
- package/clinkx-workflows/dist/dashboard/activity-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/activity-lease.d.ts +25 -0
- package/clinkx-workflows/dist/dashboard/activity-lease.js +34 -0
- package/clinkx-workflows/dist/dashboard/activity-lease.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/activity-reader.d.ts +86 -0
- package/clinkx-workflows/dist/dashboard/activity-reader.js +480 -0
- package/clinkx-workflows/dist/dashboard/activity-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/api-routes.d.ts +36 -0
- package/clinkx-workflows/dist/dashboard/api-routes.js +1047 -0
- package/clinkx-workflows/dist/dashboard/api-routes.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/artifact-index.d.ts +37 -0
- package/clinkx-workflows/dist/dashboard/artifact-index.js +145 -0
- package/clinkx-workflows/dist/dashboard/artifact-index.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/challenge-store.d.ts +31 -0
- package/clinkx-workflows/dist/dashboard/auth/challenge-store.js +65 -0
- package/clinkx-workflows/dist/dashboard/auth/challenge-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/controller.d.ts +26 -0
- package/clinkx-workflows/dist/dashboard/auth/controller.js +596 -0
- package/clinkx-workflows/dist/dashboard/auth/controller.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/credential-store.d.ts +60 -0
- package/clinkx-workflows/dist/dashboard/auth/credential-store.js +776 -0
- package/clinkx-workflows/dist/dashboard/auth/credential-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.d.ts +9 -0
- package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js +44 -0
- package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/session-store.d.ts +17 -0
- package/clinkx-workflows/dist/dashboard/auth/session-store.js +77 -0
- package/clinkx-workflows/dist/dashboard/auth/session-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/auth/webauthn-service.d.ts +25 -0
- package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js +71 -0
- package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/context-run-liveness.d.ts +37 -0
- package/clinkx-workflows/dist/dashboard/context-run-liveness.js +49 -0
- package/clinkx-workflows/dist/dashboard/context-run-liveness.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.d.ts +222 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.js +1178 -0
- package/clinkx-workflows/dist/dashboard/context-run-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/contracts.d.ts +8936 -0
- package/clinkx-workflows/dist/dashboard/contracts.js +1157 -0
- package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/generation.d.ts +14 -0
- package/clinkx-workflows/dist/dashboard/generation.js +41 -0
- package/clinkx-workflows/dist/dashboard/generation.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/index.d.ts +28 -0
- package/clinkx-workflows/dist/dashboard/index.js +16 -0
- package/clinkx-workflows/dist/dashboard/index.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.d.ts +101 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.js +748 -0
- package/clinkx-workflows/dist/dashboard/lazy-content.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +303 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.js +221 -0
- package/clinkx-workflows/dist/dashboard/progress-report-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-reader.d.ts +50 -0
- package/clinkx-workflows/dist/dashboard/progress-report-reader.js +581 -0
- package/clinkx-workflows/dist/dashboard/progress-report-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.d.ts +89 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js +71 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.d.ts +28 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.js +110 -0
- package/clinkx-workflows/dist/dashboard/progress-report-status-store.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/read-model.d.ts +86 -0
- package/clinkx-workflows/dist/dashboard/read-model.js +440 -0
- package/clinkx-workflows/dist/dashboard/read-model.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/request-admission.d.ts +31 -0
- package/clinkx-workflows/dist/dashboard/request-admission.js +126 -0
- package/clinkx-workflows/dist/dashboard/request-admission.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/scanner.d.ts +75 -0
- package/clinkx-workflows/dist/dashboard/scanner.js +1110 -0
- package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/server-factory.d.ts +50 -0
- package/clinkx-workflows/dist/dashboard/server-factory.js +431 -0
- package/clinkx-workflows/dist/dashboard/server-factory.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +2611 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.js +480 -0
- package/clinkx-workflows/dist/dashboard/session-log-contract.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-identity.d.ts +21 -0
- package/clinkx-workflows/dist/dashboard/session-log-identity.js +101 -0
- package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-journal.d.ts +43 -0
- package/clinkx-workflows/dist/dashboard/session-log-journal.js +522 -0
- package/clinkx-workflows/dist/dashboard/session-log-journal.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-reader.d.ts +61 -0
- package/clinkx-workflows/dist/dashboard/session-log-reader.js +378 -0
- package/clinkx-workflows/dist/dashboard/session-log-reader.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +6 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js +367 -0
- package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/session-log-retention.d.ts +23 -0
- package/clinkx-workflows/dist/dashboard/session-log-retention.js +212 -0
- package/clinkx-workflows/dist/dashboard/session-log-retention.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/static-responses.d.ts +10 -0
- package/clinkx-workflows/dist/dashboard/static-responses.js +94 -0
- package/clinkx-workflows/dist/dashboard/static-responses.js.map +1 -0
- package/clinkx-workflows/dist/dashboard/worker-lock-observer.d.ts +28 -0
- package/clinkx-workflows/dist/dashboard/worker-lock-observer.js +241 -0
- package/clinkx-workflows/dist/dashboard/worker-lock-observer.js.map +1 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/index-DOkMXTfB.js +25 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/clinkx-workflows/dist/dashboard-ui/assets/style-ChJsn0KD.css +1 -0
- package/clinkx-workflows/dist/dashboard-ui/contracts/council-plan-review-input-v1.json +158 -0
- package/clinkx-workflows/dist/dashboard-ui/index.html +15 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Mono-OFL.txt +93 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Sans-OFL.txt +93 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/JetBrains-Mono-OFL.txt +93 -0
- package/clinkx-workflows/dist/dashboard-ui/licenses/NOTICE.md +32 -0
- package/clinkx-workflows/dist/dashboard-ui/theme-init.js +8 -0
- package/clinkx-workflows/dist/definition-normalizer.d.ts +19 -0
- package/clinkx-workflows/dist/definition-normalizer.js.map +1 -1
- package/clinkx-workflows/dist/delegate-validate.d.ts +78 -0
- package/clinkx-workflows/dist/delegate-validate.js +97 -0
- package/clinkx-workflows/dist/delegate-validate.js.map +1 -0
- package/clinkx-workflows/dist/engine.d.ts +141 -0
- package/clinkx-workflows/dist/engine.js +882 -28
- package/clinkx-workflows/dist/engine.js.map +1 -1
- package/clinkx-workflows/dist/errors.d.ts +2 -0
- package/clinkx-workflows/dist/errors.js.map +1 -1
- package/clinkx-workflows/dist/fingerprint.d.ts +1 -1
- package/clinkx-workflows/dist/fingerprint.js +1 -1
- package/clinkx-workflows/dist/index.d.ts +19 -0
- package/clinkx-workflows/dist/index.js +15 -0
- package/clinkx-workflows/dist/index.js.map +1 -1
- package/clinkx-workflows/dist/loader.d.ts +9 -3
- package/clinkx-workflows/dist/loader.js +92 -21
- package/clinkx-workflows/dist/loader.js.map +1 -1
- package/clinkx-workflows/dist/plan-review/admission.d.ts +160 -0
- package/clinkx-workflows/dist/plan-review/admission.js +290 -0
- package/clinkx-workflows/dist/plan-review/admission.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/context-policy.d.ts +55 -0
- package/clinkx-workflows/dist/plan-review/context-policy.js +146 -0
- package/clinkx-workflows/dist/plan-review/context-policy.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/contract.d.ts +763 -0
- package/clinkx-workflows/dist/plan-review/contract.js +289 -0
- package/clinkx-workflows/dist/plan-review/contract.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/digest.d.ts +68 -0
- package/clinkx-workflows/dist/plan-review/digest.js +241 -0
- package/clinkx-workflows/dist/plan-review/digest.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/discovery.d.ts +45 -0
- package/clinkx-workflows/dist/plan-review/discovery.js +36 -0
- package/clinkx-workflows/dist/plan-review/discovery.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/execution-contract.d.ts +298 -0
- package/clinkx-workflows/dist/plan-review/execution-contract.js +194 -0
- package/clinkx-workflows/dist/plan-review/execution-contract.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/expander.d.ts +48 -0
- package/clinkx-workflows/dist/plan-review/expander.js +243 -0
- package/clinkx-workflows/dist/plan-review/expander.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/projector.d.ts +126 -0
- package/clinkx-workflows/dist/plan-review/projector.js +437 -0
- package/clinkx-workflows/dist/plan-review/projector.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/publication.d.ts +95 -0
- package/clinkx-workflows/dist/plan-review/publication.js +182 -0
- package/clinkx-workflows/dist/plan-review/publication.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/ranking-reducer.d.ts +34 -0
- package/clinkx-workflows/dist/plan-review/ranking-reducer.js +139 -0
- package/clinkx-workflows/dist/plan-review/ranking-reducer.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/resume.d.ts +38 -0
- package/clinkx-workflows/dist/plan-review/resume.js +122 -0
- package/clinkx-workflows/dist/plan-review/resume.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/rubric.d.ts +7 -0
- package/clinkx-workflows/dist/plan-review/rubric.js +24 -0
- package/clinkx-workflows/dist/plan-review/rubric.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/run-state.d.ts +398 -0
- package/clinkx-workflows/dist/plan-review/run-state.js +183 -0
- package/clinkx-workflows/dist/plan-review/run-state.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/runtime-policy.d.ts +74 -0
- package/clinkx-workflows/dist/plan-review/runtime-policy.js +186 -0
- package/clinkx-workflows/dist/plan-review/runtime-policy.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/snapshot.d.ts +81 -0
- package/clinkx-workflows/dist/plan-review/snapshot.js +170 -0
- package/clinkx-workflows/dist/plan-review/snapshot.js.map +1 -0
- package/clinkx-workflows/dist/plan-review/stage-error.d.ts +19 -0
- package/clinkx-workflows/dist/plan-review/stage-error.js +20 -0
- package/clinkx-workflows/dist/plan-review/stage-error.js.map +1 -0
- package/clinkx-workflows/dist/post-processors/index.d.ts +20 -0
- package/clinkx-workflows/dist/post-processors/index.js +21 -0
- package/clinkx-workflows/dist/post-processors/index.js.map +1 -0
- package/clinkx-workflows/dist/prompt-budget.d.ts +7 -0
- package/clinkx-workflows/dist/prompt-budget.js +7 -1
- package/clinkx-workflows/dist/prompt-budget.js.map +1 -1
- package/clinkx-workflows/dist/reducers/index.js +2 -0
- package/clinkx-workflows/dist/reducers/index.js.map +1 -1
- package/clinkx-workflows/dist/run-lock-heartbeat.d.ts +2 -0
- package/clinkx-workflows/dist/run-lock-heartbeat.js +3 -0
- package/clinkx-workflows/dist/run-lock-heartbeat.js.map +1 -0
- package/clinkx-workflows/dist/run-summary/cards/council-plan-review.d.ts +34 -0
- package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js +74 -0
- package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js.map +1 -0
- package/clinkx-workflows/dist/run-summary/cards/index.js +5 -0
- package/clinkx-workflows/dist/run-summary/cards/index.js.map +1 -1
- package/clinkx-workflows/dist/run-summary.d.ts +13 -0
- package/clinkx-workflows/dist/run-summary.js +29 -1
- package/clinkx-workflows/dist/run-summary.js.map +1 -1
- package/clinkx-workflows/dist/schema.d.ts +76 -21
- package/clinkx-workflows/dist/schema.js +10 -1
- package/clinkx-workflows/dist/schema.js.map +1 -1
- package/clinkx-workflows/dist/seed-terms.d.ts +30 -0
- package/clinkx-workflows/dist/seed-terms.js +176 -0
- package/clinkx-workflows/dist/seed-terms.js.map +1 -0
- package/clinkx-workflows/dist/server.d.ts +5 -2
- package/clinkx-workflows/dist/server.js +82 -52
- package/clinkx-workflows/dist/server.js.map +1 -1
- package/clinkx-workflows/dist/shutdown.d.ts +18 -20
- package/clinkx-workflows/dist/shutdown.js +43 -61
- package/clinkx-workflows/dist/shutdown.js.map +1 -1
- package/clinkx-workflows/dist/state-schema.d.ts +38 -0
- package/clinkx-workflows/dist/state.d.ts +170 -3
- package/clinkx-workflows/dist/state.js +1275 -28
- package/clinkx-workflows/dist/state.js.map +1 -1
- package/clinkx-workflows/dist/transport.d.ts +15 -0
- package/clinkx-workflows/dist/transport.js +19 -10
- package/clinkx-workflows/dist/transport.js.map +1 -1
- package/clinkx-workflows/dist/types.d.ts +14 -2
- package/clinkx-workflows/dist/validators/council.d.ts +318 -85
- package/clinkx-workflows/dist/validators/council.js +52 -13
- package/clinkx-workflows/dist/validators/council.js.map +1 -1
- package/clinkx-workflows/dist/validators/index.d.ts +1 -0
- package/clinkx-workflows/dist/validators/index.js +3 -0
- package/clinkx-workflows/dist/validators/index.js.map +1 -1
- package/clinkx-workflows/dist/validators/plan-review.d.ts +398 -0
- package/clinkx-workflows/dist/validators/plan-review.js +316 -0
- package/clinkx-workflows/dist/validators/plan-review.js.map +1 -0
- package/clinkx-workflows/dist/workflow-receipt.js +188 -4
- package/clinkx-workflows/dist/workflow-receipt.js.map +1 -1
- package/clinkx-workflows/dist/workflow-tools.d.ts +48 -3
- package/clinkx-workflows/dist/workflow-tools.js +1402 -59
- package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
- package/clinkx-workflows/dist/workspace-signature.d.ts +16 -0
- package/clinkx-workflows/dist/workspace-signature.js +388 -0
- package/clinkx-workflows/dist/workspace-signature.js.map +1 -0
- package/clinkx-workflows/templates/council/answer-specialist.txt +2 -1
- package/clinkx-workflows/templates/council/code-review-chairman.txt +13 -0
- package/clinkx-workflows/templates/council/code-review-cross-reviewer.txt +4 -1
- package/clinkx-workflows/templates/council/code-review-reviewer.txt +19 -3
- package/clinkx-workflows/templates/council/debug-debugger.txt +2 -1
- package/clinkx-workflows/templates/council/discover-analyst.txt +2 -1
- package/clinkx-workflows/templates/council/member.txt +2 -1
- package/clinkx-workflows/templates/council/plan-review-chairman.txt +68 -0
- package/clinkx-workflows/templates/council/plan-review-cross-reviewer.txt +51 -0
- package/clinkx-workflows/templates/council/plan-review-reviewer.txt +60 -0
- package/clinkx-workflows/templates/plan-review/chairman.txt +48 -0
- package/clinkx-workflows/templates/plan-review/cross-review.txt +52 -0
- package/clinkx-workflows/templates/plan-review/ranking.txt +16 -0
- package/clinkx-workflows/templates/plan-review/reviewer.txt +57 -0
- package/clinkx-workflows/workflows/council-answer.yaml +97 -8
- package/clinkx-workflows/workflows/council-code-review.yaml +103 -8
- package/clinkx-workflows/workflows/council-debug.yaml +97 -8
- package/clinkx-workflows/workflows/council-default.yaml +97 -8
- package/clinkx-workflows/workflows/council-discover.yaml +97 -8
- package/clinkx-workflows/workflows/council-plan-review.yaml +250 -0
- package/clinkx-workflows/workflows/plan-review-receipts.yaml +230 -0
- package/conf/adapters/agy.json +49 -0
- package/conf/adapters/claude.json +5 -0
- package/conf/adapters/codex-app-server.json +44 -0
- package/conf/adapters/codex.json +7 -2
- package/conf/adapters/glm.json +5 -0
- package/conf/adapters/grok.json +60 -0
- package/conf/adapters/hapi/agy.json +52 -0
- package/conf/adapters/hapi/claude.json +5 -0
- package/conf/adapters/hapi/codex.json +7 -2
- package/conf/adapters/hapi/glm.json +5 -0
- package/dist/activity-journal.d.ts +42 -0
- package/dist/activity-journal.js +381 -0
- package/dist/activity-journal.js.map +1 -0
- package/dist/allowed-roots.d.ts +41 -0
- package/dist/allowed-roots.js +125 -3
- package/dist/allowed-roots.js.map +1 -1
- package/dist/codex/activity-session-log.d.ts +14 -0
- package/dist/codex/activity-session-log.js +206 -0
- package/dist/codex/activity-session-log.js.map +1 -0
- package/dist/codex/agent-tools.d.ts +23 -0
- package/dist/codex/agent-tools.js +1270 -0
- package/dist/codex/agent-tools.js.map +1 -0
- package/dist/codex/app-server-client.d.ts +76 -0
- package/dist/codex/app-server-client.js +582 -0
- package/dist/codex/app-server-client.js.map +1 -0
- package/dist/codex/app-server-registry.d.ts +389 -0
- package/dist/codex/app-server-registry.js +1407 -0
- package/dist/codex/app-server-registry.js.map +1 -0
- package/dist/codex/client-types.d.ts +109 -0
- package/dist/codex/client-types.js +2 -0
- package/dist/codex/client-types.js.map +1 -0
- package/dist/codex/protocol.d.ts +5375 -0
- package/dist/codex/protocol.js +614 -0
- package/dist/codex/protocol.js.map +1 -0
- package/dist/codex/session.d.ts +263 -0
- package/dist/codex/session.js +466 -0
- package/dist/codex/session.js.map +1 -0
- package/dist/config.d.ts +185 -19
- package/dist/config.js +82 -16
- package/dist/config.js.map +1 -1
- package/dist/context-builder/anchors.d.ts +1 -0
- package/dist/context-builder/anchors.js +3 -0
- package/dist/context-builder/anchors.js.map +1 -1
- package/dist/context-builder/bundle.d.ts +35 -2
- package/dist/context-builder/bundle.js +342 -2417
- package/dist/context-builder/bundle.js.map +1 -1
- package/dist/context-builder/codemap.d.ts +5 -0
- package/dist/context-builder/codemap.js +387 -13
- package/dist/context-builder/codemap.js.map +1 -1
- package/dist/context-builder/config.d.ts +1 -0
- package/dist/context-builder/config.js +12 -0
- package/dist/context-builder/config.js.map +1 -1
- package/dist/context-builder/context-run-catalog.d.ts +17 -0
- package/dist/context-builder/context-run-catalog.js +99 -0
- package/dist/context-builder/context-run-catalog.js.map +1 -0
- package/dist/context-builder/delegate-packet.d.ts +37 -0
- package/dist/context-builder/delegate-packet.js +359 -67
- package/dist/context-builder/delegate-packet.js.map +1 -1
- package/dist/context-builder/delegate-validation.d.ts +10 -1
- package/dist/context-builder/delegate-validation.js +153 -41
- package/dist/context-builder/delegate-validation.js.map +1 -1
- package/dist/context-builder/delegate.d.ts +2 -0
- package/dist/context-builder/delegate.js +1 -0
- package/dist/context-builder/delegate.js.map +1 -1
- package/dist/context-builder/diagnostics.d.ts +11 -2
- package/dist/context-builder/diagnostics.js +39 -6
- package/dist/context-builder/diagnostics.js.map +1 -1
- package/dist/context-builder/discovery.d.ts +29 -1
- package/dist/context-builder/discovery.js +475 -175
- package/dist/context-builder/discovery.js.map +1 -1
- package/dist/context-builder/globs.js +10 -10
- package/dist/context-builder/globs.js.map +1 -1
- package/dist/context-builder/handler.d.ts +26 -2
- package/dist/context-builder/handler.js +1158 -2416
- package/dist/context-builder/handler.js.map +1 -1
- package/dist/context-builder/lifecycle-contract.d.ts +424 -0
- package/dist/context-builder/lifecycle-contract.js +523 -0
- package/dist/context-builder/lifecycle-contract.js.map +1 -0
- package/dist/context-builder/lifecycle-journal.d.ts +62 -0
- package/dist/context-builder/lifecycle-journal.js +88 -0
- package/dist/context-builder/lifecycle-journal.js.map +1 -0
- package/dist/context-builder/lifecycle-observer.d.ts +102 -0
- package/dist/context-builder/lifecycle-observer.js +258 -0
- package/dist/context-builder/lifecycle-observer.js.map +1 -0
- package/dist/context-builder/lifecycle-reconcile.d.ts +32 -0
- package/dist/context-builder/lifecycle-reconcile.js +191 -0
- package/dist/context-builder/lifecycle-reconcile.js.map +1 -0
- package/dist/context-builder/lifecycle-recovery.d.ts +2 -0
- package/dist/context-builder/lifecycle-recovery.js +14 -0
- package/dist/context-builder/lifecycle-recovery.js.map +1 -0
- package/dist/context-builder/path-signals.js +9 -5
- package/dist/context-builder/path-signals.js.map +1 -1
- package/dist/context-builder/progress.d.ts +3 -1
- package/dist/context-builder/progress.js +23 -0
- package/dist/context-builder/progress.js.map +1 -1
- package/dist/context-builder/receipt.d.ts +6 -4
- package/dist/context-builder/receipt.js +12 -1
- package/dist/context-builder/receipt.js.map +1 -1
- package/dist/context-builder/refine-handler.d.ts +14 -0
- package/dist/context-builder/refine-handler.js +790 -0
- package/dist/context-builder/refine-handler.js.map +1 -0
- package/dist/context-builder/review-coverage.d.ts +84 -0
- package/dist/context-builder/review-coverage.js +437 -0
- package/dist/context-builder/review-coverage.js.map +1 -0
- package/dist/context-builder/schema.d.ts +239 -1
- package/dist/context-builder/schema.js +112 -6
- package/dist/context-builder/schema.js.map +1 -1
- package/dist/context-builder/secrets.d.ts +28 -0
- package/dist/context-builder/secrets.js +1473 -44
- package/dist/context-builder/secrets.js.map +1 -1
- package/dist/context-builder/selection.d.ts +7 -3
- package/dist/context-builder/selection.js +200 -738
- package/dist/context-builder/selection.js.map +1 -1
- package/dist/context-builder/session-log-identity.d.ts +1 -0
- package/dist/context-builder/session-log-identity.js +66 -0
- package/dist/context-builder/session-log-identity.js.map +1 -0
- package/dist/context-builder/slice-range-math.d.ts +16 -0
- package/dist/context-builder/slice-range-math.js +137 -0
- package/dist/context-builder/slice-range-math.js.map +1 -0
- package/dist/context-builder/status.d.ts +22 -1
- package/dist/context-builder/status.js +59 -4
- package/dist/context-builder/status.js.map +1 -1
- package/dist/context-builder/storage.d.ts +35 -2
- package/dist/context-builder/storage.js +271 -14
- package/dist/context-builder/storage.js.map +1 -1
- package/dist/context-builder/token-estimate.d.ts +13 -2
- package/dist/context-builder/token-estimate.js +77 -4
- package/dist/context-builder/token-estimate.js.map +1 -1
- package/dist/context-builder/versions.d.ts +29 -0
- package/dist/context-builder/versions.js +30 -0
- package/dist/context-builder/versions.js.map +1 -0
- package/dist/context-builder/workflow-runner.d.ts +69 -0
- package/dist/context-builder/workflow-runner.js +1051 -0
- package/dist/context-builder/workflow-runner.js.map +1 -0
- package/dist/context-builder/write-watchdog.d.ts +14 -0
- package/dist/context-builder/write-watchdog.js +38 -0
- package/dist/context-builder/write-watchdog.js.map +1 -0
- package/dist/dashboard-adapter-catalog.d.ts +9 -0
- package/dist/dashboard-adapter-catalog.js +46 -0
- package/dist/dashboard-adapter-catalog.js.map +1 -0
- package/dist/dashboard-cli.d.ts +79 -0
- package/dist/dashboard-cli.js +585 -0
- package/dist/dashboard-cli.js.map +1 -0
- package/dist/dashboard-companion/client.d.ts +36 -0
- package/dist/dashboard-companion/client.js +210 -0
- package/dist/dashboard-companion/client.js.map +1 -0
- package/dist/dashboard-companion/config.d.ts +8 -0
- package/dist/dashboard-companion/config.js +52 -0
- package/dist/dashboard-companion/config.js.map +1 -0
- package/dist/dashboard-companion/http-proxy.d.ts +10 -0
- package/dist/dashboard-companion/http-proxy.js +234 -0
- package/dist/dashboard-companion/http-proxy.js.map +1 -0
- package/dist/dashboard-companion/protocol.d.ts +80 -0
- package/dist/dashboard-companion/protocol.js +455 -0
- package/dist/dashboard-companion/protocol.js.map +1 -0
- package/dist/dashboard-companion/standalone-assets.d.ts +12 -0
- package/dist/dashboard-companion/standalone-assets.js +72 -0
- package/dist/dashboard-companion/standalone-assets.js.map +1 -0
- package/dist/dashboard-companion/standalone-entry.d.ts +1 -0
- package/dist/dashboard-companion/standalone-entry.js +17 -0
- package/dist/dashboard-companion/standalone-entry.js.map +1 -0
- package/dist/dashboard-gateway/config.d.ts +9 -0
- package/dist/dashboard-gateway/config.js +44 -0
- package/dist/dashboard-gateway/config.js.map +1 -0
- package/dist/dashboard-gateway/index.d.ts +11 -0
- package/dist/dashboard-gateway/index.js +755 -0
- package/dist/dashboard-gateway/index.js.map +1 -0
- package/dist/dashboard-gateway/main.d.ts +1 -0
- package/dist/dashboard-gateway/main.js +25 -0
- package/dist/dashboard-gateway/main.js.map +1 -0
- package/dist/dashboard-pairing-token.d.ts +1 -0
- package/dist/dashboard-pairing-token.js +5 -0
- package/dist/dashboard-pairing-token.js.map +1 -0
- package/dist/elaris/client.d.ts +73 -0
- package/dist/elaris/client.js +825 -0
- package/dist/elaris/client.js.map +1 -0
- package/dist/elaris/config.d.ts +18 -0
- package/dist/elaris/config.js +75 -0
- package/dist/elaris/config.js.map +1 -0
- package/dist/elaris/errors.d.ts +41 -0
- package/dist/elaris/errors.js +205 -0
- package/dist/elaris/errors.js.map +1 -0
- package/dist/elaris/github-repo.d.ts +18 -0
- package/dist/elaris/github-repo.js +89 -0
- package/dist/elaris/github-repo.js.map +1 -0
- package/dist/elaris/index.d.ts +10 -0
- package/dist/elaris/index.js +11 -0
- package/dist/elaris/index.js.map +1 -0
- package/dist/elaris/redact.d.ts +89 -0
- package/dist/elaris/redact.js +423 -0
- package/dist/elaris/redact.js.map +1 -0
- package/dist/elaris/run-id.d.ts +5 -0
- package/dist/elaris/run-id.js +20 -0
- package/dist/elaris/run-id.js.map +1 -0
- package/dist/elaris/status.d.ts +45 -0
- package/dist/elaris/status.js +122 -0
- package/dist/elaris/status.js.map +1 -0
- package/dist/elaris/tool-schemas.d.ts +314 -0
- package/dist/elaris/tool-schemas.js +208 -0
- package/dist/elaris/tool-schemas.js.map +1 -0
- package/dist/elaris/tools.d.ts +73 -0
- package/dist/elaris/tools.js +1065 -0
- package/dist/elaris/tools.js.map +1 -0
- package/dist/elaris/types.d.ts +1541 -0
- package/dist/elaris/types.js +252 -0
- package/dist/elaris/types.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -1
- package/dist/handler.d.ts +4 -0
- package/dist/handler.js +10 -1
- package/dist/handler.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/local-clink-backend.d.ts +3 -0
- package/dist/local-clink-backend.js +4 -0
- package/dist/local-clink-backend.js.map +1 -1
- package/dist/package-info.d.ts +2 -0
- package/dist/package-info.js +18 -0
- package/dist/package-info.js.map +1 -0
- package/dist/parsers/codex-jsonl.js +48 -3
- package/dist/parsers/codex-jsonl.js.map +1 -1
- package/dist/parsers/extract.d.ts +10 -1
- package/dist/parsers/extract.js +11 -3
- package/dist/parsers/extract.js.map +1 -1
- package/dist/pipeline.d.ts +4 -0
- package/dist/pipeline.js +129 -8
- package/dist/pipeline.js.map +1 -1
- package/dist/run-dir.d.ts +24 -0
- package/dist/run-dir.js +201 -2
- package/dist/run-dir.js.map +1 -1
- package/dist/runner.d.ts +13 -0
- package/dist/runner.js +184 -15
- package/dist/runner.js.map +1 -1
- package/dist/safety.d.ts +4 -4
- package/dist/safety.js +4 -4
- package/dist/schema.d.ts +7 -7
- package/dist/server.d.ts +1 -0
- package/dist/server.js +359 -17
- package/dist/server.js.map +1 -1
- package/dist/session-log-stream.d.ts +18 -0
- package/dist/session-log-stream.js +319 -0
- package/dist/session-log-stream.js.map +1 -0
- package/package.json +46 -5
- package/conf/adapters/gemini.json +0 -44
- package/conf/adapters/hapi/gemini.json +0 -47
- package/dist/parsers/gemini-json.d.ts +0 -2
- package/dist/parsers/gemini-json.js +0 -72
- package/dist/parsers/gemini-json.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InvalidParamsError } from "../errors.js";
|
|
2
|
-
|
|
2
|
+
import { isAlias, isMap, isScalar, isSeq, parseAllDocuments, } from "yaml";
|
|
3
|
+
const PRIVATE_KEY_BLOCK_PATTERN = /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/;
|
|
3
4
|
const WELL_KNOWN_TOKEN_PATTERNS = [
|
|
4
5
|
{ provenance: "aws_access_key_id", pattern: /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/ },
|
|
5
6
|
{ provenance: "github_token", pattern: /\bgh[opusr]_[A-Za-z0-9_]{30,}\b/ },
|
|
@@ -10,7 +11,17 @@ const WELL_KNOWN_TOKEN_PATTERNS = [
|
|
|
10
11
|
{ provenance: "npm_token", pattern: /\bnpm_[A-Za-z0-9]{30,}\b/ },
|
|
11
12
|
{ provenance: "slack_token", pattern: /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/ },
|
|
12
13
|
];
|
|
13
|
-
const
|
|
14
|
+
const ASSIGNMENT_KEY_PATTERN = /(?<![A-Za-z0-9_$-])(?:(["'])([A-Za-z_][A-Za-z0-9_-]*)\1|((?:[A-Za-z_$][A-Za-z0-9_$]*(?:[ \t]*(?:\.|::)[ \t]*[A-Za-z_$][A-Za-z0-9_$]*)*[ \t]*)?\[[ \t]*(["'])([A-Za-z_][A-Za-z0-9_-]*)\4[ \t]*\])|([A-Za-z_$][A-Za-z0-9_$-]*(?:[ \t]*(?:\.|::)[ \t]*[A-Za-z_$][A-Za-z0-9_$-]*)*))(?=[ \t]*[:=])/gi;
|
|
15
|
+
const SENSITIVE_ASSIGNMENT_KEY_PATTERN = /(?:^|[_-])(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)$/i;
|
|
16
|
+
const REDACTION_MARKER = "[redacted-sensitive]";
|
|
17
|
+
const YAML_BLOCK_HEADER_PATTERN_SOURCE = String.raw `(?:(?:&[A-Za-z_][A-Za-z0-9_.-]*|!<[^>\r\n]+>|!!?[A-Za-z_][A-Za-z0-9_.:/-]*)[ \t]+)*[|>](?:[+-][1-9]?|[1-9][+-]?)?`;
|
|
18
|
+
const MAX_YAML_DOCUMENTS = 32;
|
|
19
|
+
const MAX_YAML_NODES = 20_000;
|
|
20
|
+
const MAX_YAML_DEPTH = 128;
|
|
21
|
+
const MAX_YAML_SOURCE_CHARS = 5_000_000;
|
|
22
|
+
const MAX_JS_KEY_LOOKBACK = 32_768;
|
|
23
|
+
const MAX_TS_TYPE_CHARS = 4_096;
|
|
24
|
+
const MAX_TS_TYPE_DEPTH = 32;
|
|
14
25
|
const PLACEHOLDER_VALUES = new Set([
|
|
15
26
|
"changeme",
|
|
16
27
|
"example",
|
|
@@ -77,20 +88,9 @@ export function scanHighConfidenceSecretContent(text) {
|
|
|
77
88
|
findings.push(contentFinding(provenance, line));
|
|
78
89
|
}
|
|
79
90
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (match == null) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
const value = match[3] ?? "";
|
|
87
|
-
const quote = match[2] ?? "";
|
|
88
|
-
if (quote.length === 0 && looksLikeCodeExpression(value)) {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (isHighConfidenceAssignedSecret(value)) {
|
|
92
|
-
findings.push(contentFinding("assignment_secret", index + 1));
|
|
93
|
-
break;
|
|
91
|
+
for (const assignment of iterateAssignmentSecrets(text)) {
|
|
92
|
+
if (assignment.confidence === "high") {
|
|
93
|
+
findings.push(contentFinding("assignment_secret", assignment.line));
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
return dedupeFindings(findings);
|
|
@@ -99,24 +99,120 @@ export function isBlockingSecretFinding(finding) {
|
|
|
99
99
|
return finding.provenance !== "assignment_secret";
|
|
100
100
|
}
|
|
101
101
|
export function redactHighConfidenceAssignmentSecrets(text) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
const secretAssignments = [...iterateAssignmentSecrets(text)].filter((assignment) => assignment.confidence === "high").sort((left, right) => left.valueStart - right.valueStart);
|
|
103
|
+
let redacted = text;
|
|
104
|
+
for (let index = secretAssignments.length - 1; index >= 0; index--) {
|
|
105
|
+
const assignment = secretAssignments[index];
|
|
106
|
+
if (assignment == null) {
|
|
107
|
+
continue;
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
109
|
+
redacted = `${redacted.slice(0, assignment.valueStart)}${assignment.replacement ?? REDACTION_MARKER}${redacted.slice(assignment.valueEnd)}`;
|
|
110
|
+
}
|
|
111
|
+
return redacted;
|
|
111
112
|
}
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Sanitizes one bounded text body. The residual callback is deliberately a
|
|
115
|
+
* separate lexical detector and runs for every opt-out sanitization, including
|
|
116
|
+
* content the primary assignment grammar did not classify.
|
|
117
|
+
*/
|
|
118
|
+
export function sanitizeHighConfidenceSecretContent(text, residualDetector = hasConservativeResidualSecret) {
|
|
119
|
+
const findings = scanHighConfidenceSecretContent(text);
|
|
120
|
+
if (findings.some(isBlockingSecretFinding)) {
|
|
121
|
+
return { status: "blocked", content: "", findings };
|
|
122
|
+
}
|
|
114
123
|
if (findings.length === 0) {
|
|
124
|
+
if (residualDetector(text)) {
|
|
125
|
+
return {
|
|
126
|
+
status: "blocked",
|
|
127
|
+
content: "",
|
|
128
|
+
findings: [contentFinding("residual_secret", 1)],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { status: "unchanged", content: text, findings };
|
|
132
|
+
}
|
|
133
|
+
const content = redactHighConfidenceAssignmentSecrets(text);
|
|
134
|
+
if (residualDetector(content)) {
|
|
135
|
+
return {
|
|
136
|
+
status: "blocked",
|
|
137
|
+
content: "",
|
|
138
|
+
findings: [contentFinding("residual_secret", 1)],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return { status: "redacted", content, findings };
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Independent fail-closed backstop for every opt-out content body. This does
|
|
145
|
+
* not call the assignment iterator: it recognizes sensitive key contexts and
|
|
146
|
+
* YAML block continuations with a separate, intentionally conservative lexer.
|
|
147
|
+
*/
|
|
148
|
+
export function hasConservativeResidualSecret(text) {
|
|
149
|
+
if (PRIVATE_KEY_BLOCK_PATTERN.test(text)) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
for (const { pattern } of WELL_KNOWN_TOKEN_PATTERNS) {
|
|
153
|
+
pattern.lastIndex = 0;
|
|
154
|
+
if (pattern.test(text)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const yaml = analyzeYamlStructuralSecrets(text);
|
|
159
|
+
if (yaml.unsafeSecretRange) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (yaml.parseFailed && hasMalformedYamlSensitiveSecret(text)) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
if (yaml.recognized) {
|
|
166
|
+
return yaml.matches.some((match) => match.confidence === "high");
|
|
167
|
+
}
|
|
168
|
+
const js = analyzeJsStructuralSecrets(text, false);
|
|
169
|
+
if (js.unsafeSensitiveContext ||
|
|
170
|
+
js.matches.some((match) => match.confidence === "high")) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return [...iterateAssignmentSecrets(text)].some((match) => match.confidence === "high");
|
|
174
|
+
}
|
|
175
|
+
export function hasDisallowedSecretMaterial(text, allowSecrets) {
|
|
176
|
+
if (allowSecrets === true) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
return hasHighConfidenceSecret(text) || hasConservativeResidualSecret(text);
|
|
180
|
+
}
|
|
181
|
+
export function assertNoHighConfidenceSecretsInTask(task) {
|
|
182
|
+
if (!hasDisallowedSecretMaterial(task, false)) {
|
|
115
183
|
return;
|
|
116
184
|
}
|
|
117
|
-
const
|
|
185
|
+
const findings = scanHighConfidenceSecretContent(task);
|
|
186
|
+
const provenances = findings.length === 0
|
|
187
|
+
? "residual_secret"
|
|
188
|
+
: findings.map((finding) => finding.provenance).join(", ");
|
|
118
189
|
throw new InvalidParamsError(`context_builder task contains high-confidence secret material (${provenances})`);
|
|
119
190
|
}
|
|
191
|
+
export function assertTaskSecretsGated(task, allowSecrets) {
|
|
192
|
+
if (hasDisallowedSecretMaterial(task, allowSecrets)) {
|
|
193
|
+
assertNoHighConfidenceSecretsInTask(task);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export function resolveAllowSecrets(explicit) {
|
|
197
|
+
if (typeof explicit === "boolean") {
|
|
198
|
+
return explicit;
|
|
199
|
+
}
|
|
200
|
+
const raw = process.env.CLINKX_CONTEXT_ALLOW_SECRETS;
|
|
201
|
+
if (raw == null) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
const value = raw.trim().toLowerCase();
|
|
205
|
+
if (value === "") {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
if (value === "false" || value === "0" || value === "no" || value === "off") {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
if (value === "true" || value === "1" || value === "yes" || value === "on") {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
throw new InvalidParamsError("CLINKX_CONTEXT_ALLOW_SECRETS must be one of true/false/1/0/yes/no/on/off (case-insensitive)");
|
|
215
|
+
}
|
|
120
216
|
export function hasHighConfidenceSecret(text) {
|
|
121
217
|
return scanHighConfidenceSecretContent(text).length > 0;
|
|
122
218
|
}
|
|
@@ -157,9 +253,86 @@ function findPatternLine(text, pattern) {
|
|
|
157
253
|
return undefined;
|
|
158
254
|
}
|
|
159
255
|
function looksLikeCodeExpression(value) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
256
|
+
const expression = unwrapReferenceExpression(value);
|
|
257
|
+
const plainReference = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(expression);
|
|
258
|
+
const explicitlyNamedReference = /(?:reference|ref|source|provider)[0-9]*$/i.test(expression);
|
|
259
|
+
const memberReference = /^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:::|\.)[A-Za-z_$][A-Za-z0-9_$]*)+$/.test(expression);
|
|
260
|
+
return (looksLikeTsTypeExpression(expression) ||
|
|
261
|
+
/^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:::|\.)[A-Za-z_$][A-Za-z0-9_$]*)*\([^"'`\r\n]*\)$/.test(expression) ||
|
|
262
|
+
(plainReference &&
|
|
263
|
+
!SENSITIVE_ASSIGNMENT_KEY_PATTERN.test(expression) &&
|
|
264
|
+
(explicitlyNamedReference ||
|
|
265
|
+
!isHighConfidenceAssignedSecret(expression))) ||
|
|
266
|
+
(memberReference && !looksLikeCredentialShapedDottedLiteral(expression)) ||
|
|
267
|
+
(/^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\?\.|\.)[A-Za-z_$][A-Za-z0-9_$]*(?:\([^"'`\r\n]*\))?|\[[A-Za-z_$][A-Za-z0-9_$]*\])+(?:\?\.[A-Za-z_$][A-Za-z0-9_$]*\([^"'`\r\n]*\))*$/.test(expression) && !looksLikeCredentialShapedDottedLiteral(expression)) ||
|
|
268
|
+
/^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\?\.|\.)[A-Za-z_$][A-Za-z0-9_$]*(?:\([^"'`\r\n]*\))?|\[[A-Za-z_$][A-Za-z0-9_$]*\])*(?:\?\.[A-Za-z_$][A-Za-z0-9_$]*\([^"'`\r\n]*\))*\s*(?:\?\?|\|\|)\s*(?:["']{2}|undefined|null);?$/.test(expression) ||
|
|
269
|
+
/^(?:\$\{[A-Za-z_][A-Za-z0-9_]*(?::-[^}\r\n]*)?\}|\$[A-Za-z_][A-Za-z0-9_]*)$/.test(expression));
|
|
270
|
+
}
|
|
271
|
+
function looksLikeTsTypeExpression(value, allowNamedType = false) {
|
|
272
|
+
const type = value.trim().replace(/;\s*$/, "").trim();
|
|
273
|
+
if (type.length === 0 ||
|
|
274
|
+
/["'`=]/.test(type) ||
|
|
275
|
+
classifyTsTypeAnnotation(type) !== "valid") {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return (/\b(?:any|bigint|boolean|never|null|number|object|string|symbol|undefined|unknown|void)\b/.test(type) ||
|
|
279
|
+
/\b(?:keyof|readonly|typeof)\b/.test(type) ||
|
|
280
|
+
/[<>{}\[\]|&?]/.test(type) ||
|
|
281
|
+
(allowNamedType &&
|
|
282
|
+
/^[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\.|::)[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(type)));
|
|
283
|
+
}
|
|
284
|
+
function unwrapReferenceExpression(value) {
|
|
285
|
+
let expression = value.trim();
|
|
286
|
+
for (let count = 0; count < 16; count++) {
|
|
287
|
+
const before = expression;
|
|
288
|
+
expression = expression
|
|
289
|
+
.replace(/\s+(?:as|satisfies)\s+(?:const|[A-Za-z_$][A-Za-z0-9_$]*(?:\s*<[^<>\r\n]+>)?)$/, "")
|
|
290
|
+
.trim();
|
|
291
|
+
if (hasBalancedOuterParentheses(expression)) {
|
|
292
|
+
expression = expression.slice(1, -1).trim();
|
|
293
|
+
}
|
|
294
|
+
expression = expression
|
|
295
|
+
.replace(/^<[A-Za-z_$][A-Za-z0-9_$]*(?:<[^<>\r\n]+>)?>\s*/, "")
|
|
296
|
+
.trim();
|
|
297
|
+
if (expression === before) {
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return expression;
|
|
302
|
+
}
|
|
303
|
+
function hasBalancedOuterParentheses(value) {
|
|
304
|
+
if (!value.startsWith("(") || !value.endsWith(")")) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
let depth = 0;
|
|
308
|
+
for (let index = 0; index < value.length; index++) {
|
|
309
|
+
if (value[index] === "(") {
|
|
310
|
+
depth++;
|
|
311
|
+
}
|
|
312
|
+
else if (value[index] === ")") {
|
|
313
|
+
depth--;
|
|
314
|
+
if (depth === 0 && index < value.length - 1) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
if (depth < 0) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return depth === 0;
|
|
323
|
+
}
|
|
324
|
+
function looksLikeCredentialShapedDottedLiteral(value) {
|
|
325
|
+
const segments = value.split(".");
|
|
326
|
+
if (segments.length < 2) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
if (/^(?:sk|pk|rk|pat)$/i.test(segments[0] ?? "")) {
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
const terminal = segments.at(-1) ?? "";
|
|
333
|
+
return (terminal.length >= 16 &&
|
|
334
|
+
!terminal.includes("_") &&
|
|
335
|
+
isHighConfidenceAssignedSecret(terminal));
|
|
163
336
|
}
|
|
164
337
|
function isHighConfidenceAssignedSecret(value) {
|
|
165
338
|
const normalized = value.trim().toLowerCase();
|
|
@@ -169,6 +342,12 @@ function isHighConfidenceAssignedSecret(value) {
|
|
|
169
342
|
if (PLACEHOLDER_VALUES.has(normalized)) {
|
|
170
343
|
return false;
|
|
171
344
|
}
|
|
345
|
+
if (normalized === REDACTION_MARKER) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
if (normalized === REDACTION_MARKER.slice(1, -1)) {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
172
351
|
if (/^(?:x+|\*+|0+|1+|a+)$/.test(normalized)) {
|
|
173
352
|
return false;
|
|
174
353
|
}
|
|
@@ -177,24 +356,1274 @@ function isHighConfidenceAssignedSecret(value) {
|
|
|
177
356
|
}
|
|
178
357
|
return shannonEntropy(value) >= 3;
|
|
179
358
|
}
|
|
180
|
-
function
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
359
|
+
function isSensitiveDecodedKey(value) {
|
|
360
|
+
const key = value.trim();
|
|
361
|
+
const terminal = key.split(".").at(-1) ?? key;
|
|
362
|
+
return [key, terminal].some((candidate) => /^[A-Za-z_$][A-Za-z0-9_$-]*$/.test(candidate) &&
|
|
363
|
+
SENSITIVE_ASSIGNMENT_KEY_PATTERN.test(candidate));
|
|
364
|
+
}
|
|
365
|
+
function isRedactionMarkerValue(value) {
|
|
366
|
+
const normalized = value.trim();
|
|
367
|
+
return (normalized.length > 0 &&
|
|
368
|
+
(normalized.replaceAll(REDACTION_MARKER, "").trim().length === 0 ||
|
|
369
|
+
normalized
|
|
370
|
+
.replaceAll(REDACTION_MARKER.slice(1, -1), "")
|
|
371
|
+
.trim().length === 0));
|
|
372
|
+
}
|
|
373
|
+
function analyzeYamlStructuralSecrets(text) {
|
|
374
|
+
if (text.length > MAX_YAML_SOURCE_CHARS) {
|
|
375
|
+
return { ...emptyYamlStructuralAnalysis(), recognized: true, exhausted: true };
|
|
376
|
+
}
|
|
377
|
+
let documents;
|
|
378
|
+
try {
|
|
379
|
+
documents = parseAllDocuments(text, {
|
|
380
|
+
prettyErrors: false,
|
|
381
|
+
strict: false,
|
|
382
|
+
uniqueKeys: false,
|
|
383
|
+
});
|
|
184
384
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (quote.length === 0 && looksLikeCodeExpression(value)) {
|
|
188
|
-
return line;
|
|
385
|
+
catch {
|
|
386
|
+
return { ...emptyYamlStructuralAnalysis(), parseFailed: true };
|
|
189
387
|
}
|
|
190
|
-
if (
|
|
191
|
-
return
|
|
388
|
+
if (documents.some((document) => document.errors.length > 0)) {
|
|
389
|
+
return {
|
|
390
|
+
...emptyYamlStructuralAnalysis(),
|
|
391
|
+
parseFailed: true,
|
|
392
|
+
};
|
|
192
393
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
394
|
+
if (documents.length === 0 || documents.length > MAX_YAML_DOCUMENTS) {
|
|
395
|
+
return {
|
|
396
|
+
...emptyYamlStructuralAnalysis(),
|
|
397
|
+
exhausted: documents.length > MAX_YAML_DOCUMENTS,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const collectionDocument = documents.some((document) => isMap(document.contents) || isSeq(document.contents));
|
|
401
|
+
if (!collectionDocument) {
|
|
402
|
+
return emptyYamlStructuralAnalysis();
|
|
403
|
+
}
|
|
404
|
+
const analysis = {
|
|
405
|
+
recognized: false,
|
|
406
|
+
exhausted: false,
|
|
407
|
+
unsafeSecretRange: false,
|
|
408
|
+
parseFailed: false,
|
|
409
|
+
matches: [],
|
|
410
|
+
};
|
|
411
|
+
let hasSensitiveKey = false;
|
|
412
|
+
let nodesSeen = 0;
|
|
413
|
+
let currentDocument = documents[0];
|
|
414
|
+
const resolveMappingKeyScalar = (node) => {
|
|
415
|
+
let current = node;
|
|
416
|
+
const active = new Set();
|
|
417
|
+
for (let depth = 0; depth <= MAX_YAML_DEPTH; depth++) {
|
|
418
|
+
if (current == null || ++nodesSeen > MAX_YAML_NODES) {
|
|
419
|
+
analysis.exhausted = true;
|
|
420
|
+
return undefined;
|
|
421
|
+
}
|
|
422
|
+
if (isScalar(current)) {
|
|
423
|
+
return current;
|
|
424
|
+
}
|
|
425
|
+
if (!isAlias(current) || active.has(current) || currentDocument == null) {
|
|
426
|
+
analysis.exhausted = true;
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
active.add(current);
|
|
430
|
+
current = current.resolve(currentDocument);
|
|
431
|
+
}
|
|
432
|
+
analysis.exhausted = true;
|
|
433
|
+
return undefined;
|
|
434
|
+
};
|
|
435
|
+
const visit = (node, sensitiveAncestor, depth, unsafeScalarRange) => {
|
|
436
|
+
if (node == null || analysis.exhausted) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
nodesSeen++;
|
|
440
|
+
if (nodesSeen > MAX_YAML_NODES || depth > MAX_YAML_DEPTH) {
|
|
441
|
+
analysis.exhausted = true;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (isMap(node)) {
|
|
445
|
+
for (const pair of node.items) {
|
|
446
|
+
const pairKey = pair.key;
|
|
447
|
+
if (sensitiveAncestor) {
|
|
448
|
+
visit(pairKey, true, depth + 1, false);
|
|
449
|
+
}
|
|
450
|
+
const keyNode = isScalar(pairKey)
|
|
451
|
+
? pairKey
|
|
452
|
+
: isAlias(pairKey)
|
|
453
|
+
? resolveMappingKeyScalar(pairKey)
|
|
454
|
+
: undefined;
|
|
455
|
+
if (isAlias(pairKey) && keyNode == null) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const decodedKey = isScalar(keyNode)
|
|
459
|
+
? String(keyNode.value ?? "")
|
|
460
|
+
: undefined;
|
|
461
|
+
const sensitiveKey = decodedKey != null && isSensitiveDecodedKey(decodedKey);
|
|
462
|
+
hasSensitiveKey ||= sensitiveKey;
|
|
463
|
+
const pairValue = pair.value;
|
|
464
|
+
const keyEnd = pairKey?.range?.[1];
|
|
465
|
+
const valueStart = pairValue?.range?.[0];
|
|
466
|
+
const valueGap = keyEnd == null || valueStart == null
|
|
467
|
+
? ""
|
|
468
|
+
: text.slice(keyEnd, valueStart);
|
|
469
|
+
if (sensitiveKey &&
|
|
470
|
+
(valueGap.match(/\n/g)?.length ?? 0) > 256) {
|
|
471
|
+
if (isScalar(pairValue)) {
|
|
472
|
+
visit(pairValue, true, depth + 1, true);
|
|
473
|
+
}
|
|
474
|
+
analysis.exhausted = true;
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
visit(pairValue, sensitiveAncestor || sensitiveKey, depth + 1, sensitiveKey &&
|
|
478
|
+
isScalar(pairValue) &&
|
|
479
|
+
pairValue.type === "PLAIN" &&
|
|
480
|
+
/\r|\n/.test(valueGap));
|
|
481
|
+
}
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (isSeq(node)) {
|
|
485
|
+
for (const item of node.items) {
|
|
486
|
+
visit(item, sensitiveAncestor, depth + 1, false);
|
|
487
|
+
}
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (!sensitiveAncestor) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
if (isAlias(node)) {
|
|
494
|
+
if (currentDocument == null) {
|
|
495
|
+
analysis.exhausted = true;
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const referenceStatus = yamlReferenceTreeStatus(node, currentDocument);
|
|
499
|
+
if (referenceStatus === "safe") {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
if (referenceStatus === "secret") {
|
|
503
|
+
analysis.unsafeSecretRange = true;
|
|
504
|
+
}
|
|
505
|
+
else {
|
|
506
|
+
analysis.exhausted = true;
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (!isScalar(node)) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
let value;
|
|
514
|
+
if (typeof node.value === "string") {
|
|
515
|
+
value = node.value;
|
|
516
|
+
}
|
|
517
|
+
else if (node.value instanceof Uint8Array) {
|
|
518
|
+
const decoded = new TextDecoder("utf-8", { fatal: false }).decode(node.value);
|
|
519
|
+
const encoded = typeof node.source === "string" ? node.source : "";
|
|
520
|
+
value = isHighConfidenceAssignedSecret(decoded) ? decoded : encoded;
|
|
521
|
+
}
|
|
522
|
+
else if (node.value == null ||
|
|
523
|
+
typeof node.value === "number" ||
|
|
524
|
+
typeof node.value === "boolean") {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
analysis.exhausted = true;
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (isRedactionMarkerValue(value) ||
|
|
532
|
+
(node.type === "PLAIN" && looksLikeCodeExpression(value.trim())) ||
|
|
533
|
+
!isHighConfidenceAssignedSecret(value)) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const range = node.range;
|
|
537
|
+
const valueStart = range?.[0];
|
|
538
|
+
const valueEnd = range?.[1];
|
|
539
|
+
if (valueStart == null ||
|
|
540
|
+
valueEnd == null ||
|
|
541
|
+
valueStart < 0 ||
|
|
542
|
+
valueEnd <= valueStart ||
|
|
543
|
+
valueEnd > text.length) {
|
|
544
|
+
analysis.unsafeSecretRange = true;
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (unsafeScalarRange) {
|
|
548
|
+
analysis.unsafeSecretRange = true;
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
analysis.matches.push({
|
|
552
|
+
keyStart: valueStart,
|
|
553
|
+
keyEnd: valueStart,
|
|
554
|
+
valueStart,
|
|
555
|
+
valueEnd,
|
|
556
|
+
line: lineNumberAtOffset(text, valueStart),
|
|
557
|
+
confidence: "high",
|
|
558
|
+
replacement: redactYamlScalarSource(text.slice(valueStart, valueEnd)),
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
for (const document of documents) {
|
|
562
|
+
currentDocument = document;
|
|
563
|
+
visit(document.contents, false, 0, false);
|
|
564
|
+
}
|
|
565
|
+
analysis.recognized = hasSensitiveKey;
|
|
566
|
+
return analysis;
|
|
567
|
+
}
|
|
568
|
+
function yamlReferenceTreeStatus(node, document) {
|
|
569
|
+
const active = new Set();
|
|
570
|
+
const memoizedSafe = new Set();
|
|
571
|
+
let remaining = MAX_YAML_NODES;
|
|
572
|
+
const visit = (current, depth) => {
|
|
573
|
+
if (current == null || depth > MAX_YAML_DEPTH) {
|
|
574
|
+
return "incomplete";
|
|
575
|
+
}
|
|
576
|
+
if (memoizedSafe.has(current)) {
|
|
577
|
+
return "safe";
|
|
578
|
+
}
|
|
579
|
+
if (--remaining < 0 || active.has(current)) {
|
|
580
|
+
return "incomplete";
|
|
581
|
+
}
|
|
582
|
+
active.add(current);
|
|
583
|
+
let status;
|
|
584
|
+
if (isAlias(current)) {
|
|
585
|
+
const resolved = current.resolve(document);
|
|
586
|
+
status = visit(resolved, depth + 1);
|
|
587
|
+
}
|
|
588
|
+
else if (isSeq(current)) {
|
|
589
|
+
status = combineYamlReferenceStatuses(current.items.map((item) => visit(item, depth + 1)));
|
|
590
|
+
}
|
|
591
|
+
else if (isMap(current)) {
|
|
592
|
+
status = combineYamlReferenceStatuses(current.items.flatMap((pair) => [
|
|
593
|
+
visit(pair.key, depth + 1),
|
|
594
|
+
visit(pair.value, depth + 1),
|
|
595
|
+
]));
|
|
596
|
+
}
|
|
597
|
+
else if (!isScalar(current)) {
|
|
598
|
+
status = "incomplete";
|
|
599
|
+
}
|
|
600
|
+
else if (current.value == null ||
|
|
601
|
+
typeof current.value === "number" ||
|
|
602
|
+
typeof current.value === "boolean") {
|
|
603
|
+
status = "safe";
|
|
604
|
+
}
|
|
605
|
+
else if (current.value instanceof Uint8Array) {
|
|
606
|
+
const decoded = new TextDecoder("utf-8", { fatal: false }).decode(current.value);
|
|
607
|
+
const encoded = typeof current.source === "string" ? current.source : "";
|
|
608
|
+
status = isHighConfidenceAssignedSecret(decoded) ||
|
|
609
|
+
isHighConfidenceAssignedSecret(encoded)
|
|
610
|
+
? "secret"
|
|
611
|
+
: "safe";
|
|
612
|
+
}
|
|
613
|
+
else if (typeof current.value !== "string") {
|
|
614
|
+
status = "incomplete";
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
const safe = isRedactionMarkerValue(current.value) ||
|
|
618
|
+
!isHighConfidenceAssignedSecret(current.value) ||
|
|
619
|
+
(current.type === "PLAIN" &&
|
|
620
|
+
looksLikeCodeExpression(current.value.trim()));
|
|
621
|
+
status = safe ? "safe" : "secret";
|
|
622
|
+
}
|
|
623
|
+
active.delete(current);
|
|
624
|
+
if (status === "safe") {
|
|
625
|
+
memoizedSafe.add(current);
|
|
626
|
+
}
|
|
627
|
+
return status;
|
|
628
|
+
};
|
|
629
|
+
return visit(node, 0);
|
|
630
|
+
}
|
|
631
|
+
function combineYamlReferenceStatuses(statuses) {
|
|
632
|
+
if (statuses.includes("secret")) {
|
|
633
|
+
return "secret";
|
|
634
|
+
}
|
|
635
|
+
return statuses.includes("incomplete") ? "incomplete" : "safe";
|
|
636
|
+
}
|
|
637
|
+
function emptyYamlStructuralAnalysis() {
|
|
638
|
+
return {
|
|
639
|
+
recognized: false,
|
|
640
|
+
exhausted: false,
|
|
641
|
+
parseFailed: false,
|
|
642
|
+
unsafeSecretRange: false,
|
|
643
|
+
matches: [],
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function hasMalformedYamlSensitiveSecret(text) {
|
|
647
|
+
// Valid TypeScript class fields are not YAML merely because a nested type
|
|
648
|
+
// property has a sensitive name. Remove only declarations whose bounded RHS
|
|
649
|
+
// is an unambiguous reference; literal/object RHS values remain visible to
|
|
650
|
+
// the fail-closed malformed-content check below.
|
|
651
|
+
const withoutSafeTypedFields = maskConfidentlySafeTypeOnlyFields(maskConfidentlySafeTypedFields(text));
|
|
652
|
+
for (const valueStart of malformedYamlSensitiveValueStarts(withoutSafeTypedFields)) {
|
|
653
|
+
const span = malformedYamlValueSpan(withoutSafeTypedFields, valueStart);
|
|
654
|
+
const expression = span.trim().replace(/,$/, "").trim();
|
|
655
|
+
if (looksLikeCodeExpression(expression)) {
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
if (spanContainsHighEntropyLiteral(span)) {
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
function malformedYamlValueSpan(text, valueStart) {
|
|
665
|
+
const firstLineEnd = lineEndIndex(text, valueStart);
|
|
666
|
+
const firstLine = text.slice(valueStart, firstLineEnd);
|
|
667
|
+
const firstValue = firstLine.replace(/^[ \t]*/, "");
|
|
668
|
+
const continues = firstValue.length === 0 ||
|
|
669
|
+
firstValue.startsWith("#") ||
|
|
670
|
+
new RegExp(`^(?:&[A-Za-z0-9_.-]+[ \\t]+|!!?[^ \\t\\r\\n]+[ \\t]+|!<[^>\\r\\n]+>[ \\t]+)*${YAML_BLOCK_HEADER_PATTERN_SOURCE}(?:[ \\t]+#.*)?$`).test(firstValue);
|
|
671
|
+
if (!continues) {
|
|
672
|
+
return firstLine;
|
|
673
|
+
}
|
|
674
|
+
const keyLineStart = text.lastIndexOf("\n", valueStart - 1) + 1;
|
|
675
|
+
const keyIndent = leadingIndentWidth(text.slice(keyLineStart, valueStart));
|
|
676
|
+
let cursor = firstLineEnd;
|
|
677
|
+
let end = firstLineEnd;
|
|
678
|
+
let lines = 0;
|
|
679
|
+
while (cursor < text.length && lines <= 512 && cursor - valueStart < 32_768) {
|
|
680
|
+
const newlineLength = text.startsWith("\r\n", cursor)
|
|
681
|
+
? 2
|
|
682
|
+
: text[cursor] === "\n"
|
|
683
|
+
? 1
|
|
684
|
+
: 0;
|
|
685
|
+
if (newlineLength === 0) {
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
688
|
+
const lineStart = cursor + newlineLength;
|
|
689
|
+
const lineEnd = lineEndIndex(text, lineStart);
|
|
690
|
+
const line = text.slice(lineStart, lineEnd);
|
|
691
|
+
if (line.trim().length > 0 && leadingIndentWidth(line) <= keyIndent) {
|
|
692
|
+
break;
|
|
693
|
+
}
|
|
694
|
+
end = lineEnd;
|
|
695
|
+
cursor = lineEnd;
|
|
696
|
+
lines++;
|
|
697
|
+
}
|
|
698
|
+
return text.slice(valueStart, end);
|
|
699
|
+
}
|
|
700
|
+
function maskConfidentlySafeTypeOnlyFields(text) {
|
|
701
|
+
const typeOnlyField = /(^|[;{}\r\n])([ \t]*(?:(?:public|private|protected|static|readonly|declare|abstract|override)[ \t]+)*[A-Za-z_$][A-Za-z0-9_$-]*(?:[?!])?[ \t]*:[ \t]*)([\s\S]{1,4096}?);/gm;
|
|
702
|
+
return text.replace(typeOnlyField, (whole, boundary, _prefix, type, offset) => {
|
|
703
|
+
if (!isInsideBoundedTypeBody(text, offset + boundary.length) ||
|
|
704
|
+
!looksLikeTsTypeExpression(type, true)) {
|
|
705
|
+
return whole;
|
|
706
|
+
}
|
|
707
|
+
return `${boundary}${whole.slice(boundary.length).replace(/[^\r\n]/g, " ")}`;
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
function maskConfidentlySafeTypedFields(text) {
|
|
711
|
+
const masked = maskJsCommentTrivia(text);
|
|
712
|
+
const safeSpans = [];
|
|
713
|
+
let quote;
|
|
714
|
+
let escaped = false;
|
|
715
|
+
for (let equalsAt = 0; equalsAt < masked.length; equalsAt++) {
|
|
716
|
+
const character = masked[equalsAt] ?? "";
|
|
717
|
+
if (quote != null) {
|
|
718
|
+
if (!escaped && character === quote) {
|
|
719
|
+
quote = undefined;
|
|
720
|
+
}
|
|
721
|
+
escaped = !escaped && character === "\\";
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
725
|
+
quote = character;
|
|
726
|
+
escaped = false;
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (character !== "=" ||
|
|
730
|
+
/[=!<>]/.test(masked[equalsAt - 1] ?? "") ||
|
|
731
|
+
/[=>]/.test(masked[equalsAt + 1] ?? "")) {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const windowStart = Math.max(0, equalsAt - MAX_JS_KEY_LOOKBACK);
|
|
735
|
+
const field = findBoundedTypedField(masked.slice(windowStart, equalsAt), windowStart);
|
|
736
|
+
if (field == null || field === "exhausted") {
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
const rhs = residualJsValueSpan(text, masked, equalsAt + 1);
|
|
740
|
+
if (rhs.exhausted) {
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
const value = reconstructStaticJsValue(rhs.content)?.trim();
|
|
744
|
+
if (value == null || !isSafeTypedFieldReference(rhs.content, value)) {
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
safeSpans.push({
|
|
748
|
+
start: field.start,
|
|
749
|
+
end: equalsAt + 1 + rhs.content.length,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (safeSpans.length === 0) {
|
|
753
|
+
return text;
|
|
754
|
+
}
|
|
755
|
+
// Parser/lexer ranges are UTF-16 offsets, so preserve code-unit indexing.
|
|
756
|
+
const characters = text.split("");
|
|
757
|
+
for (const span of safeSpans) {
|
|
758
|
+
for (let index = span.start; index < span.end; index++) {
|
|
759
|
+
if (characters[index] !== "\n" && characters[index] !== "\r") {
|
|
760
|
+
characters[index] = " ";
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return characters.join("");
|
|
765
|
+
}
|
|
766
|
+
function isSafeTypedFieldReference(source, value) {
|
|
767
|
+
const expression = unwrapReferenceExpression(value);
|
|
768
|
+
const sourceExpression = unwrapReferenceExpression(maskJsCommentTrivia(source));
|
|
769
|
+
const staticEnvReference = /^(?:process\.env\[["'][A-Za-z_][A-Za-z0-9_]*["']\]|Deno\.env\.get\(["'][A-Za-z_][A-Za-z0-9_]*["']\))$/.test(sourceExpression);
|
|
770
|
+
return (staticEnvReference ||
|
|
771
|
+
(!/["'`]/.test(maskJsCommentTrivia(source)) &&
|
|
772
|
+
(looksLikeCodeExpression(expression) ||
|
|
773
|
+
/^keyof\s+typeof\s+[A-Za-z_$][A-Za-z0-9_$]*(?:(?:\.|::)[A-Za-z_$][A-Za-z0-9_$]*)*$/.test(expression))));
|
|
774
|
+
}
|
|
775
|
+
function malformedYamlSensitiveValueStarts(text) {
|
|
776
|
+
const starts = [];
|
|
777
|
+
const keyToken = String.raw `[A-Za-z_$][A-Za-z0-9_$-]*(?:\.[A-Za-z_$][A-Za-z0-9_$-]*)*`;
|
|
778
|
+
for (const match of text.matchAll(/^[ \t]*(["'])((?:\\.|(?!\1)[^\r\n])*)\1[ \t]*:/gm)) {
|
|
779
|
+
const decoded = match[1] === '"'
|
|
780
|
+
? decodeJsString(match[2] ?? "")
|
|
781
|
+
: (match[2] ?? "").replaceAll("''", "'");
|
|
782
|
+
if (decoded != null && isSensitiveDecodedKey(decoded)) {
|
|
783
|
+
starts.push((match.index ?? 0) + match[0].length);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
for (const match of text.matchAll(new RegExp(String.raw `^[ \t]*(${keyToken})[ \t]*:`, "gm"))) {
|
|
787
|
+
if (isSensitiveDecodedKey(match[1] ?? "")) {
|
|
788
|
+
starts.push((match.index ?? 0) + match[0].length);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
const sensitiveAliases = new Set();
|
|
792
|
+
for (const match of text.matchAll(new RegExp(String.raw `&([A-Za-z0-9_.-]+)(?:[ \t]+(?:!!?[^ \t\r\n]+|!<[^>\r\n]+>))*[ \t]+(${keyToken})`, "g"))) {
|
|
793
|
+
if (isSensitiveDecodedKey(match[2] ?? "")) {
|
|
794
|
+
sensitiveAliases.add(match[1] ?? "");
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const explicitKey = new RegExp(String.raw `^[ \t]*\?[ \t]+(?:(${keyToken})|\*([A-Za-z0-9_.-]+))[ \t]*(?:\r?\n)[ \t]*:`, "gm");
|
|
798
|
+
for (const match of text.matchAll(explicitKey)) {
|
|
799
|
+
if (isSensitiveDecodedKey(match[1] ?? "") ||
|
|
800
|
+
sensitiveAliases.has(match[2] ?? "")) {
|
|
801
|
+
starts.push((match.index ?? 0) + match[0].length);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return starts;
|
|
805
|
+
}
|
|
806
|
+
function redactYamlScalarSource(source) {
|
|
807
|
+
if (!/\r|\n/.test(source)) {
|
|
808
|
+
const leading = /^[ \t]*/.exec(source)?.[0] ?? "";
|
|
809
|
+
const trailing = /[ \t]*$/.exec(source)?.[0] ?? "";
|
|
810
|
+
const body = source.slice(leading.length, source.length - trailing.length);
|
|
811
|
+
const quote = body[0] === '"' || body[0] === "'" ? body[0] : undefined;
|
|
812
|
+
return `${leading}${quote ?? ""}${REDACTION_MARKER}${quote ?? ""}${trailing}`;
|
|
813
|
+
}
|
|
814
|
+
const firstLineEnd = lineEndIndex(source, 0);
|
|
815
|
+
const firstLine = source.slice(0, firstLineEnd);
|
|
816
|
+
if (new RegExp(`^[ \\t]*${YAML_BLOCK_HEADER_PATTERN_SOURCE}(?:[ \\t]+#.*)?$`).test(firstLine)) {
|
|
817
|
+
const newlineLength = source.startsWith("\r\n", firstLineEnd) ? 2 : 1;
|
|
818
|
+
return `${source.slice(0, firstLineEnd + newlineLength)}${redactYamlBlockPayload(source.slice(firstLineEnd + newlineLength))}`;
|
|
819
|
+
}
|
|
820
|
+
const trimmed = source.trim();
|
|
821
|
+
const quote = trimmed[0] === '"' || trimmed[0] === "'" ? trimmed[0] : undefined;
|
|
822
|
+
if (quote != null && trimmed.endsWith(quote)) {
|
|
823
|
+
const leading = /^[ \t]*/.exec(source)?.[0] ?? "";
|
|
824
|
+
const lineBreaks = source.match(/\r\n|\n/g) ?? [];
|
|
825
|
+
return `${leading}${quote}${REDACTION_MARKER}${quote}${lineBreaks.join("")}`;
|
|
826
|
+
}
|
|
827
|
+
return redactYamlBlockPayload(source);
|
|
828
|
+
}
|
|
829
|
+
function lineNumberAtOffset(text, offset) {
|
|
830
|
+
let line = 1;
|
|
831
|
+
for (let index = 0; index < offset; index++) {
|
|
832
|
+
if (text[index] === "\n") {
|
|
833
|
+
line++;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return line;
|
|
837
|
+
}
|
|
838
|
+
function analyzeJsStructuralSecrets(text, primaryRedaction = true) {
|
|
839
|
+
const masked = maskJsCommentTrivia(text);
|
|
840
|
+
const matches = [];
|
|
841
|
+
let unsafeSensitiveContext = false;
|
|
842
|
+
let quote;
|
|
843
|
+
let escaped = false;
|
|
844
|
+
let scanned = 0;
|
|
845
|
+
for (let cursor = 0; cursor < masked.length; cursor++) {
|
|
846
|
+
if (++scanned > 1_000_000) {
|
|
847
|
+
return { exhausted: true, unsafeSensitiveContext, matches };
|
|
848
|
+
}
|
|
849
|
+
const character = masked[cursor] ?? "";
|
|
850
|
+
if (quote != null) {
|
|
851
|
+
if (!escaped && character === quote) {
|
|
852
|
+
quote = undefined;
|
|
853
|
+
}
|
|
854
|
+
escaped = !escaped && character === "\\";
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
858
|
+
quote = character;
|
|
859
|
+
escaped = false;
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
if (character !== "=" ||
|
|
863
|
+
/[=!<>]/.test(masked[cursor - 1] ?? "") ||
|
|
864
|
+
/[=>]/.test(masked[cursor + 1] ?? "")) {
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
const key = sensitiveJsKeyBefore(text, masked, cursor);
|
|
868
|
+
if (key === "exhausted") {
|
|
869
|
+
const span = residualJsValueSpan(text, masked, cursor + 1);
|
|
870
|
+
unsafeSensitiveContext ||=
|
|
871
|
+
hasExhaustedSensitiveJsKeyEvidence(masked, cursor) &&
|
|
872
|
+
spanContainsHighEntropyLiteral(text.slice(cursor + 1));
|
|
873
|
+
return { exhausted: true, unsafeSensitiveContext, matches };
|
|
874
|
+
}
|
|
875
|
+
if (key == null) {
|
|
876
|
+
if (hasUnclassifiableSensitiveJsKey(text, masked, cursor)) {
|
|
877
|
+
const span = residualJsValueSpan(text, masked, cursor + 1);
|
|
878
|
+
if (span.exhausted) {
|
|
879
|
+
unsafeSensitiveContext ||= spanContainsHighEntropyLiteral(text.slice(cursor + 1));
|
|
880
|
+
return { exhausted: true, unsafeSensitiveContext, matches };
|
|
881
|
+
}
|
|
882
|
+
const unsafe = spanContainsHighEntropyLiteral(span.content);
|
|
883
|
+
unsafeSensitiveContext ||= unsafe;
|
|
884
|
+
}
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
if (primaryRedaction &&
|
|
888
|
+
/\/\*|\/\/|\r|\n/.test(text.slice(key.end, cursor))) {
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
const span = residualJsValueSpan(text, masked, cursor + 1);
|
|
892
|
+
if (span.exhausted) {
|
|
893
|
+
unsafeSensitiveContext ||= spanContainsHighEntropyLiteral(text.slice(cursor + 1));
|
|
894
|
+
return { exhausted: true, unsafeSensitiveContext, matches };
|
|
895
|
+
}
|
|
896
|
+
const reconstructed = reconstructStaticJsValue(span.content);
|
|
897
|
+
const containsLiteral = /["'`]/.test(maskJsCommentTrivia(span.content));
|
|
898
|
+
if (looksLikeCodeExpression(span.content.trim())) {
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const typedFieldWindowStart = Math.max(0, cursor - MAX_JS_KEY_LOOKBACK);
|
|
902
|
+
const typedField = findBoundedTypedField(masked.slice(typedFieldWindowStart, cursor), typedFieldWindowStart);
|
|
903
|
+
const safeTypedReference = typedField != null &&
|
|
904
|
+
typedField !== "exhausted" &&
|
|
905
|
+
reconstructed != null &&
|
|
906
|
+
isSafeTypedFieldReference(span.content, reconstructed.trim());
|
|
907
|
+
if (safeTypedReference) {
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
if (reconstructed == null) {
|
|
911
|
+
const unsafe = spanContainsHighEntropyLiteral(span.content);
|
|
912
|
+
unsafeSensitiveContext ||= unsafe;
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
if ((!containsLiteral && looksLikeCodeExpression(reconstructed.trim())) ||
|
|
916
|
+
!isHighConfidenceAssignedSecret(reconstructed)) {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
const valueStart = cursor + 1;
|
|
920
|
+
const valueEnd = valueStart + span.content.length;
|
|
921
|
+
matches.push({
|
|
922
|
+
keyStart: key.start,
|
|
923
|
+
keyEnd: key.end,
|
|
924
|
+
valueStart,
|
|
925
|
+
valueEnd,
|
|
926
|
+
line: lineNumberAtOffset(text, key.start),
|
|
927
|
+
confidence: "high",
|
|
928
|
+
replacement: redactJsExpressionSource(span.content),
|
|
929
|
+
});
|
|
930
|
+
cursor = Math.max(cursor, valueEnd - 1);
|
|
931
|
+
}
|
|
932
|
+
return { exhausted: false, unsafeSensitiveContext, matches };
|
|
933
|
+
}
|
|
934
|
+
function hasExhaustedSensitiveJsKeyEvidence(masked, equalsAt) {
|
|
935
|
+
const prefix = masked.slice(0, equalsAt);
|
|
936
|
+
const keyPattern = /(?:^|[;{}])[ \t\r\n]*(?:(?:public|private|protected|static|readonly|declare|abstract|override)[ \t\r\n]+)*(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)\b/gi;
|
|
937
|
+
let lastMatch = null;
|
|
938
|
+
for (const match of prefix.matchAll(keyPattern)) {
|
|
939
|
+
lastMatch = match;
|
|
940
|
+
}
|
|
941
|
+
if (lastMatch == null) {
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
const suffix = prefix.slice((lastMatch.index ?? 0) + lastMatch[0].length);
|
|
945
|
+
return !suffix.includes(";");
|
|
946
|
+
}
|
|
947
|
+
function hasUnclassifiableSensitiveJsKey(text, masked, equalsAt) {
|
|
948
|
+
const start = Math.max(0, equalsAt - 2_048);
|
|
949
|
+
const prefix = text.slice(start, equalsAt);
|
|
950
|
+
const maskedPrefix = masked.slice(start, equalsAt);
|
|
951
|
+
const bracket = /\[[^\]\r\n]{0,384}\]\s*(?:\|\||\?\?|\+)?\s*$/.exec(maskedPrefix);
|
|
952
|
+
if (bracket != null) {
|
|
953
|
+
const rawBracket = prefix.slice(bracket.index ?? 0);
|
|
954
|
+
for (const match of rawBracket.matchAll(/(["'])((?:\\.|(?!\1)[^\r\n])*)\1/g)) {
|
|
955
|
+
const decoded = decodeJsString(match[2] ?? "");
|
|
956
|
+
if (decoded != null && isSensitiveDecodedKey(decoded)) {
|
|
957
|
+
return true;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return new RegExp(String.raw `(?:^|[;\r\n])[ \t]*(?:const|let|var)?[ \t]*(?:password|passwd|api[_-]?key|secret|token|access[_-]?token|private[_-]?key|client[_-]?secret)[ \t]*(?:\|\||\?\?|\+)[ \t]*$`, "i").test(maskedPrefix);
|
|
962
|
+
}
|
|
963
|
+
function spanContainsHighEntropyLiteral(span) {
|
|
964
|
+
for (const match of span.matchAll(/(["'`])((?:\\.|(?!\1)[\s\S])*)\1/g)) {
|
|
965
|
+
const encoded = match[2] ?? "";
|
|
966
|
+
const literal = match[1] === "`" ? encoded : decodeJsString(encoded) ?? encoded;
|
|
967
|
+
if (isHighConfidenceAssignedSecret(literal) ||
|
|
968
|
+
[...literal.matchAll(/[A-Za-z0-9_./+=-]{16,}/g)].some((part) => isHighConfidenceAssignedSecret(part[0]))) {
|
|
969
|
+
return true;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const withoutComments = maskJsCommentTrivia(span)
|
|
973
|
+
.split(/\r?\n/)
|
|
974
|
+
.map((line) => line.replace(/(?:^|[ \t])#.*$/, ""))
|
|
975
|
+
.join("\n");
|
|
976
|
+
for (const line of withoutComments.split(/\r?\n/)) {
|
|
977
|
+
const candidate = line
|
|
978
|
+
.trim()
|
|
979
|
+
.replace(/^:[ \t]*/, "")
|
|
980
|
+
.replace(/^["']/, "")
|
|
981
|
+
.replace(/["']$/, "")
|
|
982
|
+
.trim();
|
|
983
|
+
if (!isRedactionMarkerValue(candidate) &&
|
|
984
|
+
!candidate.includes(REDACTION_MARKER) &&
|
|
985
|
+
!new RegExp(String.raw `:\s*${YAML_BLOCK_HEADER_PATTERN_SOURCE}(?:\s+#.*)?$`).test(candidate) &&
|
|
986
|
+
!looksLikeCodeExpression(candidate) &&
|
|
987
|
+
isHighConfidenceAssignedSecret(candidate)) {
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return [...withoutComments.matchAll(/[A-Za-z0-9_./+=-]{16,}/g)].some((match) => !looksLikeCodeExpression(match[0]) &&
|
|
992
|
+
isHighConfidenceAssignedSecret(match[0]));
|
|
993
|
+
}
|
|
994
|
+
function sensitiveJsKeyBefore(text, masked, equalsAt) {
|
|
995
|
+
const windowStart = Math.max(0, equalsAt - MAX_JS_KEY_LOOKBACK);
|
|
996
|
+
const prefix = masked.slice(windowStart, equalsAt);
|
|
997
|
+
const typedField = findSensitiveTypedField(prefix, windowStart);
|
|
998
|
+
if (typedField != null) {
|
|
999
|
+
return typedField;
|
|
1000
|
+
}
|
|
1001
|
+
const bracket = /\[\s*(["'])((?:\\.|(?!\1)[^\r\n])*)\1\s*\]\s*$/.exec(prefix);
|
|
1002
|
+
if (bracket != null) {
|
|
1003
|
+
const encoded = bracket[2] ?? "";
|
|
1004
|
+
const decoded = decodeJsString(encoded);
|
|
1005
|
+
if (decoded != null && isSensitiveDecodedKey(decoded)) {
|
|
1006
|
+
const encodedOffset = bracket[0].indexOf(encoded);
|
|
1007
|
+
const start = windowStart + (bracket.index ?? 0) + encodedOffset;
|
|
1008
|
+
return { start, end: start + encoded.length };
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
const quoted = /(["'])((?:\\.|(?!\1)[^\r\n])*)\1\s*$/.exec(prefix);
|
|
1012
|
+
if (quoted != null) {
|
|
1013
|
+
const encoded = quoted[2] ?? "";
|
|
1014
|
+
const decoded = decodeJsString(encoded);
|
|
1015
|
+
if (decoded != null && isSensitiveDecodedKey(decoded)) {
|
|
1016
|
+
const encodedOffset = quoted[0].indexOf(encoded);
|
|
1017
|
+
const start = windowStart + (quoted.index ?? 0) + encodedOffset;
|
|
1018
|
+
return { start, end: start + encoded.length };
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
const bare = /([A-Za-z_$][A-Za-z0-9_$-]*)\s*$/.exec(prefix);
|
|
1022
|
+
const rawKey = bare?.[1];
|
|
1023
|
+
if (rawKey == null) {
|
|
1024
|
+
if (windowStart > 0) {
|
|
1025
|
+
return "exhausted";
|
|
1026
|
+
}
|
|
1027
|
+
return undefined;
|
|
1028
|
+
}
|
|
1029
|
+
if (!isSensitiveDecodedKey(rawKey)) {
|
|
1030
|
+
if (windowStart > 0) {
|
|
1031
|
+
const beforeKey = prefix
|
|
1032
|
+
.slice(0, bare?.index ?? 0)
|
|
1033
|
+
.trimEnd()
|
|
1034
|
+
.at(-1);
|
|
1035
|
+
if (beforeKey != null && !/[;{}\r\n]/.test(beforeKey)) {
|
|
1036
|
+
return "exhausted";
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return undefined;
|
|
1040
|
+
}
|
|
1041
|
+
const start = windowStart + (bare?.index ?? 0);
|
|
1042
|
+
return { start, end: start + rawKey.length };
|
|
1043
|
+
}
|
|
1044
|
+
function classifyTsTypeAnnotation(value) {
|
|
1045
|
+
const type = value.trim();
|
|
1046
|
+
if (type.length === 0) {
|
|
1047
|
+
return "unclassifiable";
|
|
1048
|
+
}
|
|
1049
|
+
const pairs = {
|
|
1050
|
+
"<": ">",
|
|
1051
|
+
"[": "]",
|
|
1052
|
+
"{": "}",
|
|
1053
|
+
"(": ")",
|
|
1054
|
+
};
|
|
1055
|
+
const closers = new Set(Object.values(pairs));
|
|
1056
|
+
const stack = [];
|
|
1057
|
+
for (let index = 0; index < type.length; index++) {
|
|
1058
|
+
const character = type[index] ?? "";
|
|
1059
|
+
if (character === ";" && stack.length === 0) {
|
|
1060
|
+
return "member_boundary";
|
|
1061
|
+
}
|
|
1062
|
+
if (index >= MAX_TS_TYPE_CHARS ||
|
|
1063
|
+
!/[A-Za-z0-9_$.,;<>\[\]{}()|&?:='" \t\r\n-]/.test(character)) {
|
|
1064
|
+
return "unclassifiable";
|
|
1065
|
+
}
|
|
1066
|
+
if (character === ">" && type[index - 1] === "=") {
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
const closer = pairs[character];
|
|
1070
|
+
if (closer != null) {
|
|
1071
|
+
if (stack.length >= MAX_TS_TYPE_DEPTH) {
|
|
1072
|
+
return "unclassifiable";
|
|
1073
|
+
}
|
|
1074
|
+
stack.push(closer);
|
|
1075
|
+
}
|
|
1076
|
+
else if (closers.has(character) && stack.pop() !== character) {
|
|
1077
|
+
return "nested_candidate";
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
return stack.length === 0 ? "valid" : "unclassifiable";
|
|
1081
|
+
}
|
|
1082
|
+
function findSensitiveTypedField(prefix, absoluteStart) {
|
|
1083
|
+
const field = findBoundedTypedField(prefix, absoluteStart);
|
|
1084
|
+
if (field === "exhausted" || field == null || field.sensitive) {
|
|
1085
|
+
return field === "exhausted" || field == null
|
|
1086
|
+
? field
|
|
1087
|
+
: { start: field.start, end: field.end };
|
|
1088
|
+
}
|
|
1089
|
+
return undefined;
|
|
1090
|
+
}
|
|
1091
|
+
function findBoundedTypedField(prefix, absoluteStart) {
|
|
1092
|
+
const pattern = /(?:^|[;{}\r\n])[ \t]*(?:(?:public|private|protected|static|readonly|declare|abstract|override)[ \t]+)*([A-Za-z_$][A-Za-z0-9_$-]*)(?:[?!])?[ \t]*:[ \t]*/g;
|
|
1093
|
+
for (const match of prefix.matchAll(pattern)) {
|
|
1094
|
+
const rawKey = match[1] ?? "";
|
|
1095
|
+
const keyOffset = match[0].lastIndexOf(rawKey);
|
|
1096
|
+
const relativeKeyStart = (match.index ?? 0) + keyOffset;
|
|
1097
|
+
if (!isInsideBoundedTypeBody(prefix, relativeKeyStart)) {
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
const typeStart = (match.index ?? 0) + match[0].length;
|
|
1101
|
+
const type = prefix.slice(typeStart).trim();
|
|
1102
|
+
const classification = classifyTsTypeAnnotation(type);
|
|
1103
|
+
if (classification === "member_boundary") {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
if (classification === "nested_candidate") {
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
if (classification === "unclassifiable") {
|
|
1110
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(rawKey) &&
|
|
1111
|
+
isSensitiveDecodedKey(rawKey)
|
|
1112
|
+
? "exhausted"
|
|
1113
|
+
: undefined;
|
|
1114
|
+
}
|
|
1115
|
+
// The first candidate whose entire suffix is a balanced type annotation is
|
|
1116
|
+
// the outer field. Nested object-type properties leave unmatched closers.
|
|
1117
|
+
const start = absoluteStart + relativeKeyStart;
|
|
1118
|
+
return {
|
|
1119
|
+
start,
|
|
1120
|
+
end: start + rawKey.length,
|
|
1121
|
+
sensitive: isSensitiveDecodedKey(rawKey),
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
return undefined;
|
|
1125
|
+
}
|
|
1126
|
+
function isInsideBoundedTypeBody(prefix, offset) {
|
|
1127
|
+
const start = Math.max(0, offset - MAX_JS_KEY_LOOKBACK);
|
|
1128
|
+
const before = prefix.slice(start, offset);
|
|
1129
|
+
let classBrace = -1;
|
|
1130
|
+
for (const match of before.matchAll(/\b(?:class|interface|type)\s+[A-Za-z_$][A-Za-z0-9_$]*[^{};]*\{/g)) {
|
|
1131
|
+
classBrace = (match.index ?? 0) + match[0].length - 1;
|
|
1132
|
+
}
|
|
1133
|
+
if (classBrace < 0) {
|
|
1134
|
+
return false;
|
|
1135
|
+
}
|
|
1136
|
+
let depth = 0;
|
|
1137
|
+
for (let index = classBrace; index < before.length; index++) {
|
|
1138
|
+
if (before[index] === "{") {
|
|
1139
|
+
depth++;
|
|
1140
|
+
}
|
|
1141
|
+
else if (before[index] === "}") {
|
|
1142
|
+
depth--;
|
|
1143
|
+
if (depth === 0) {
|
|
1144
|
+
return false;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
if (depth > MAX_TS_TYPE_DEPTH) {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
return depth > 0;
|
|
1152
|
+
}
|
|
1153
|
+
function findTsFieldAssignmentEquals(text, start, end) {
|
|
1154
|
+
for (let index = start; index < end; index++) {
|
|
1155
|
+
if (text[index] === "=" &&
|
|
1156
|
+
text[index + 1] !== ">" &&
|
|
1157
|
+
!/[=!<>]/.test(text[index - 1] ?? "")) {
|
|
1158
|
+
return index;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
return undefined;
|
|
1162
|
+
}
|
|
1163
|
+
function decodeJsString(value) {
|
|
1164
|
+
try {
|
|
1165
|
+
return value.replace(/\\(?:u\{([0-9A-Fa-f]{1,6})\}|u([0-9A-Fa-f]{4})|x([0-9A-Fa-f]{2})|([\\"'bfnrtv0]))/g, (_whole, braced, unicode, hex, simple) => {
|
|
1166
|
+
if (braced != null || unicode != null || hex != null) {
|
|
1167
|
+
const point = Number.parseInt(braced ?? unicode ?? hex, 16);
|
|
1168
|
+
return String.fromCodePoint(point);
|
|
1169
|
+
}
|
|
1170
|
+
const escapes = {
|
|
1171
|
+
"\\": "\\",
|
|
1172
|
+
'"': '"',
|
|
1173
|
+
"'": "'",
|
|
1174
|
+
b: "\b",
|
|
1175
|
+
f: "\f",
|
|
1176
|
+
n: "\n",
|
|
1177
|
+
r: "\r",
|
|
1178
|
+
t: "\t",
|
|
1179
|
+
v: "\v",
|
|
1180
|
+
"0": "\0",
|
|
1181
|
+
};
|
|
1182
|
+
return escapes[simple] ?? simple;
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
catch {
|
|
1186
|
+
return undefined;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
function reconstructStaticJsValue(source) {
|
|
1190
|
+
const masked = maskJsCommentTrivia(source);
|
|
1191
|
+
if (!/["'`]/.test(masked)) {
|
|
1192
|
+
return masked.trim();
|
|
1193
|
+
}
|
|
1194
|
+
let cursor = 0;
|
|
1195
|
+
let value = "";
|
|
1196
|
+
let fragments = 0;
|
|
1197
|
+
while (cursor < masked.length) {
|
|
1198
|
+
while (cursor < masked.length && /[\s()+]/.test(masked[cursor] ?? "")) {
|
|
1199
|
+
cursor++;
|
|
1200
|
+
}
|
|
1201
|
+
if (cursor >= masked.length) {
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
const quote = masked[cursor];
|
|
1205
|
+
if (quote !== '"' && quote !== "'" && quote !== "`") {
|
|
1206
|
+
const bare = masked.slice(cursor).trim();
|
|
1207
|
+
return fragments === 0 ? bare : undefined;
|
|
1208
|
+
}
|
|
1209
|
+
cursor++;
|
|
1210
|
+
let encoded = "";
|
|
1211
|
+
let escaped = false;
|
|
1212
|
+
let dynamicTemplate = false;
|
|
1213
|
+
while (cursor < masked.length) {
|
|
1214
|
+
const character = masked[cursor] ?? "";
|
|
1215
|
+
if (!escaped && character === quote) {
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
if (quote === "`" && !escaped && character === "$" && masked[cursor + 1] === "{") {
|
|
1219
|
+
dynamicTemplate = true;
|
|
1220
|
+
}
|
|
1221
|
+
encoded += source[cursor] ?? "";
|
|
1222
|
+
escaped = !escaped && character === "\\";
|
|
1223
|
+
cursor++;
|
|
1224
|
+
}
|
|
1225
|
+
if (masked[cursor] !== quote || dynamicTemplate) {
|
|
1226
|
+
return undefined;
|
|
1227
|
+
}
|
|
1228
|
+
const decoded = decodeJsString(encoded);
|
|
1229
|
+
if (decoded == null) {
|
|
1230
|
+
return undefined;
|
|
1231
|
+
}
|
|
1232
|
+
value += decoded;
|
|
1233
|
+
fragments++;
|
|
1234
|
+
cursor++;
|
|
1235
|
+
}
|
|
1236
|
+
return fragments > 0 ? value : undefined;
|
|
1237
|
+
}
|
|
1238
|
+
function redactJsExpressionSource(source) {
|
|
1239
|
+
const leading = /^[ \t]*/.exec(source)?.[0] ?? "";
|
|
1240
|
+
const trailing = /[ \t]*$/.exec(source)?.[0] ?? "";
|
|
1241
|
+
const trimmed = source.trim();
|
|
1242
|
+
const single = /^(?:"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*')$/.test(trimmed);
|
|
1243
|
+
const quote = single ? trimmed[0] : "";
|
|
1244
|
+
const lineBreaks = source.match(/\r\n|\n/g) ?? [];
|
|
1245
|
+
return `${leading}${quote}${REDACTION_MARKER}${quote}${lineBreaks.join("")}${trailing}`;
|
|
1246
|
+
}
|
|
1247
|
+
function* iterateAssignmentSecrets(text) {
|
|
1248
|
+
const yaml = analyzeYamlStructuralSecrets(text);
|
|
1249
|
+
if (yaml.recognized && !yaml.exhausted) {
|
|
1250
|
+
yield* yaml.matches;
|
|
1251
|
+
}
|
|
1252
|
+
const js = analyzeJsStructuralSecrets(text, true);
|
|
1253
|
+
if (!js.exhausted) {
|
|
1254
|
+
yield* js.matches;
|
|
1255
|
+
}
|
|
1256
|
+
const commentMasked = maskJsCommentTrivia(text);
|
|
1257
|
+
let line = 1;
|
|
1258
|
+
let lineCursor = 0;
|
|
1259
|
+
const keyPattern = new RegExp(ASSIGNMENT_KEY_PATTERN.source, ASSIGNMENT_KEY_PATTERN.flags);
|
|
1260
|
+
let match;
|
|
1261
|
+
while ((match = keyPattern.exec(commentMasked)) != null) {
|
|
1262
|
+
const matchStart = match.index;
|
|
1263
|
+
for (let index = lineCursor; index < matchStart; index++) {
|
|
1264
|
+
if (text[index] === "\n") {
|
|
1265
|
+
line++;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
lineCursor = matchStart;
|
|
1269
|
+
const bracketKey = match[5];
|
|
1270
|
+
const memberChain = match[6];
|
|
1271
|
+
const key = match[2] ?? bracketKey ?? memberChain?.split(/\s*(?:\.|::)\s*/).at(-1);
|
|
1272
|
+
if (key == null || !SENSITIVE_ASSIGNMENT_KEY_PATTERN.test(key)) {
|
|
1273
|
+
continue;
|
|
1274
|
+
}
|
|
1275
|
+
const quotedKey = match[1] != null;
|
|
1276
|
+
const keyOffset = match[0].lastIndexOf(key);
|
|
1277
|
+
const keyStart = matchStart + (quotedKey ? 1 : keyOffset);
|
|
1278
|
+
const keyEnd = keyStart + key.length;
|
|
1279
|
+
let cursor = matchStart + match[0].length;
|
|
1280
|
+
while (commentMasked[cursor] === " " || commentMasked[cursor] === "\t") {
|
|
1281
|
+
cursor++;
|
|
1282
|
+
}
|
|
1283
|
+
if (commentMasked[cursor] !== ":" && commentMasked[cursor] !== "=") {
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
const delimiter = commentMasked[cursor];
|
|
1287
|
+
if (delimiter === "=" && !js.exhausted) {
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
if (delimiter === ":" && yaml.recognized && !yaml.exhausted) {
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
if (delimiter === ":") {
|
|
1294
|
+
const declarationStart = text.lastIndexOf("\n", matchStart - 1) + 1;
|
|
1295
|
+
const declarationEnd = lineEndIndex(text, cursor);
|
|
1296
|
+
const typeTerminator = commentMasked.indexOf(";", cursor + 1);
|
|
1297
|
+
const typeOnlyDeclaration = typeTerminator >= 0 &&
|
|
1298
|
+
typeTerminator <= declarationEnd
|
|
1299
|
+
? commentMasked.slice(cursor + 1, typeTerminator).trim()
|
|
1300
|
+
: "";
|
|
1301
|
+
if (typeOnlyDeclaration.length > 0 &&
|
|
1302
|
+
isInsideBoundedTypeBody(commentMasked, keyStart) &&
|
|
1303
|
+
looksLikeTsTypeExpression(typeOnlyDeclaration, true)) {
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
const assignmentEquals = findTsFieldAssignmentEquals(commentMasked, cursor + 1, declarationEnd);
|
|
1307
|
+
if (assignmentEquals != null) {
|
|
1308
|
+
const typedField = findBoundedTypedField(commentMasked.slice(declarationStart, assignmentEquals), declarationStart);
|
|
1309
|
+
if (typedField != null) {
|
|
1310
|
+
continue;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
cursor++;
|
|
1315
|
+
while (commentMasked[cursor] === " " || commentMasked[cursor] === "\t") {
|
|
1316
|
+
cursor++;
|
|
1317
|
+
}
|
|
1318
|
+
const blockIndicator = delimiter === ":"
|
|
1319
|
+
? new RegExp(`^(${YAML_BLOCK_HEADER_PATTERN_SOURCE})(?=[ \\t]*(?:#|\\r?$))`, "m").exec(text.slice(cursor))
|
|
1320
|
+
: null;
|
|
1321
|
+
if (blockIndicator != null) {
|
|
1322
|
+
const block = parseYamlBlockScalar(text, matchStart, cursor, blockIndicator[1] ?? "");
|
|
1323
|
+
if (block == null) {
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
keyPattern.lastIndex = block.valueEnd;
|
|
1327
|
+
const value = text.slice(block.valueStart, block.valueEnd);
|
|
1328
|
+
yield {
|
|
1329
|
+
keyStart,
|
|
1330
|
+
keyEnd,
|
|
1331
|
+
valueStart: block.valueStart,
|
|
1332
|
+
valueEnd: block.valueEnd,
|
|
1333
|
+
line,
|
|
1334
|
+
confidence: isHighConfidenceAssignedSecret(value.trim()) ? "high" : "none",
|
|
1335
|
+
replacement: redactYamlBlockPayload(value),
|
|
1336
|
+
};
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
const quote = text[cursor] === '"' || text[cursor] === "'" ? text[cursor] : undefined;
|
|
1340
|
+
let valueStart = quote == null ? cursor : cursor + 1;
|
|
1341
|
+
let valueEnd = valueStart;
|
|
1342
|
+
if (quote == null) {
|
|
1343
|
+
if (delimiter === ":") {
|
|
1344
|
+
valueEnd = logicalYamlValueEnd(text, valueStart);
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
while (valueEnd < text.length && !/[\s;,#]/.test(text[valueEnd] ?? "")) {
|
|
1348
|
+
valueEnd++;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
else {
|
|
1353
|
+
let escaped = false;
|
|
1354
|
+
while (valueEnd < text.length) {
|
|
1355
|
+
const character = text[valueEnd];
|
|
1356
|
+
if (character === "\r" || character === "\n") {
|
|
1357
|
+
break;
|
|
1358
|
+
}
|
|
1359
|
+
if (!escaped && character === quote) {
|
|
1360
|
+
break;
|
|
1361
|
+
}
|
|
1362
|
+
if (!escaped && character === "\\") {
|
|
1363
|
+
escaped = true;
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
escaped = false;
|
|
1367
|
+
}
|
|
1368
|
+
valueEnd++;
|
|
1369
|
+
}
|
|
1370
|
+
if (text[valueEnd] !== quote) {
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
const tailStart = skipHorizontalWhitespace(text, valueEnd + 1);
|
|
1374
|
+
if (text[tailStart] === "+") {
|
|
1375
|
+
valueStart = cursor;
|
|
1376
|
+
const logicalSpan = residualJsValueSpan(text, maskJsCommentTrivia(text), cursor);
|
|
1377
|
+
valueEnd = cursor + logicalSpan.content.length;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
keyPattern.lastIndex =
|
|
1381
|
+
valueEnd + (quote != null && valueStart !== cursor ? 1 : 0);
|
|
1382
|
+
const value = text.slice(valueStart, valueEnd);
|
|
1383
|
+
const confidence = (quote != null || !looksLikeCodeExpression(value)) &&
|
|
1384
|
+
isHighConfidenceAssignedSecret(value)
|
|
1385
|
+
? "high"
|
|
1386
|
+
: "none";
|
|
1387
|
+
yield {
|
|
1388
|
+
keyStart,
|
|
1389
|
+
keyEnd,
|
|
1390
|
+
valueStart,
|
|
1391
|
+
valueEnd,
|
|
1392
|
+
line,
|
|
1393
|
+
confidence,
|
|
1394
|
+
...(/\r|\n/.test(value)
|
|
1395
|
+
? { replacement: redactYamlBlockPayload(value) }
|
|
1396
|
+
: {}),
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
function logicalYamlValueEnd(text, valueStart) {
|
|
1401
|
+
let end = valueStart;
|
|
1402
|
+
const delimiters = [];
|
|
1403
|
+
let quote;
|
|
1404
|
+
let escaped = false;
|
|
1405
|
+
while (end < text.length) {
|
|
1406
|
+
const character = text[end] ?? "";
|
|
1407
|
+
if (quote != null) {
|
|
1408
|
+
if (!escaped && character === quote) {
|
|
1409
|
+
quote = undefined;
|
|
1410
|
+
}
|
|
1411
|
+
escaped = !escaped && character === "\\";
|
|
1412
|
+
end++;
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
1416
|
+
quote = character;
|
|
1417
|
+
escaped = false;
|
|
1418
|
+
end++;
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
if (character === "/" && text[end + 1] === "*") {
|
|
1422
|
+
if (delimiters.length === 0) {
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
const commentEnd = text.indexOf("*/", end + 2);
|
|
1426
|
+
if (commentEnd < 0) {
|
|
1427
|
+
break;
|
|
1428
|
+
}
|
|
1429
|
+
end = commentEnd + 2;
|
|
1430
|
+
continue;
|
|
1431
|
+
}
|
|
1432
|
+
if (character === "(" || character === "[" || character === "{") {
|
|
1433
|
+
delimiters.push(character);
|
|
1434
|
+
end++;
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
if (character === ")" || character === "]" || character === "}") {
|
|
1438
|
+
const opener = character === ")" ? "(" : character === "]" ? "[" : "{";
|
|
1439
|
+
if (delimiters.at(-1) !== opener) {
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1442
|
+
delimiters.pop();
|
|
1443
|
+
end++;
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
if (delimiters.length === 0 && (character === "\r" || character === "\n" || character === ";" || character === ",")) {
|
|
1447
|
+
break;
|
|
1448
|
+
}
|
|
1449
|
+
if (delimiters.length === 0 &&
|
|
1450
|
+
((character === "#" &&
|
|
1451
|
+
(end === valueStart || /[ \t]/.test(text[end - 1] ?? ""))))) {
|
|
1452
|
+
break;
|
|
1453
|
+
}
|
|
1454
|
+
end++;
|
|
1455
|
+
}
|
|
1456
|
+
while (end > valueStart && /[ \t]/.test(text[end - 1] ?? "")) {
|
|
1457
|
+
end--;
|
|
1458
|
+
}
|
|
1459
|
+
return end;
|
|
1460
|
+
}
|
|
1461
|
+
function skipHorizontalWhitespace(text, start) {
|
|
1462
|
+
let cursor = start;
|
|
1463
|
+
while (text[cursor] === " " || text[cursor] === "\t") {
|
|
1464
|
+
cursor++;
|
|
1465
|
+
}
|
|
1466
|
+
return cursor;
|
|
1467
|
+
}
|
|
1468
|
+
function parseYamlBlockScalar(text, keyStart, valueCursor, indicator) {
|
|
1469
|
+
const headerEnd = lineEndIndex(text, valueCursor + indicator.length);
|
|
1470
|
+
const newlineLength = text.startsWith("\r\n", headerEnd) ? 2 : text[headerEnd] === "\n" ? 1 : 0;
|
|
1471
|
+
if (newlineLength === 0) {
|
|
1472
|
+
return undefined;
|
|
1473
|
+
}
|
|
1474
|
+
const lineStart = text.lastIndexOf("\n", keyStart - 1) + 1;
|
|
1475
|
+
const keyIndent = leadingIndentWidth(text.slice(lineStart, keyStart));
|
|
1476
|
+
const valueStart = headerEnd + newlineLength;
|
|
1477
|
+
let cursor = valueStart;
|
|
1478
|
+
let valueEnd = valueStart;
|
|
1479
|
+
while (cursor < text.length) {
|
|
1480
|
+
const end = lineEndIndex(text, cursor);
|
|
1481
|
+
const lineText = text.slice(cursor, end);
|
|
1482
|
+
if (lineText.trim().length > 0 && leadingIndentWidth(lineText) <= keyIndent) {
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
const lineBreakLength = text.startsWith("\r\n", end) ? 2 : text[end] === "\n" ? 1 : 0;
|
|
1486
|
+
valueEnd = end + lineBreakLength;
|
|
1487
|
+
cursor = valueEnd;
|
|
1488
|
+
if (lineBreakLength === 0) {
|
|
1489
|
+
break;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return { valueStart, valueEnd };
|
|
1493
|
+
}
|
|
1494
|
+
function redactYamlBlockPayload(value) {
|
|
1495
|
+
return value.replace(/([^\r\n]*)(\r\n|\n|$)/g, (whole, line, newline) => {
|
|
1496
|
+
if (whole.length === 0) {
|
|
1497
|
+
return whole;
|
|
1498
|
+
}
|
|
1499
|
+
const indent = /^[ \t]*/.exec(line)?.[0] ?? "";
|
|
1500
|
+
return line.trim().length === 0 ? `${line}${newline}` : `${indent}${REDACTION_MARKER}${newline}`;
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
function lineEndIndex(text, start) {
|
|
1504
|
+
const newline = text.indexOf("\n", start);
|
|
1505
|
+
if (newline < 0) {
|
|
1506
|
+
return text.length;
|
|
1507
|
+
}
|
|
1508
|
+
return newline > 0 && text[newline - 1] === "\r" ? newline - 1 : newline;
|
|
1509
|
+
}
|
|
1510
|
+
function leadingIndentWidth(value) {
|
|
1511
|
+
return /^[ \t]*/.exec(value)?.[0].length ?? 0;
|
|
1512
|
+
}
|
|
1513
|
+
function residualJsValueSpan(text, commentMasked, valueStart) {
|
|
1514
|
+
let cursor = valueStart;
|
|
1515
|
+
let quote;
|
|
1516
|
+
let escaped = false;
|
|
1517
|
+
let nesting = 0;
|
|
1518
|
+
let lineCount = 0;
|
|
1519
|
+
let valueSeenOnLine = false;
|
|
1520
|
+
while (cursor < text.length && cursor - valueStart < 32_768 && lineCount < 64) {
|
|
1521
|
+
const character = commentMasked[cursor] ?? "";
|
|
1522
|
+
if (quote != null) {
|
|
1523
|
+
if (!escaped && character === quote) {
|
|
1524
|
+
quote = undefined;
|
|
1525
|
+
}
|
|
1526
|
+
escaped = !escaped && character === "\\";
|
|
1527
|
+
if (character !== "\r" && character !== "\n" && !/\s/.test(character)) {
|
|
1528
|
+
valueSeenOnLine = true;
|
|
1529
|
+
}
|
|
1530
|
+
cursor++;
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
1534
|
+
quote = character;
|
|
1535
|
+
valueSeenOnLine = true;
|
|
1536
|
+
cursor++;
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
if (character === "(" || character === "[" || character === "{") {
|
|
1540
|
+
nesting++;
|
|
1541
|
+
}
|
|
1542
|
+
else if (character === ")" || character === "]" || character === "}") {
|
|
1543
|
+
nesting = Math.max(0, nesting - 1);
|
|
1544
|
+
}
|
|
1545
|
+
else if ((character === ";" || character === ",") && nesting === 0) {
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
if (character === "\r" || character === "\n") {
|
|
1549
|
+
const before = commentMasked.slice(valueStart, cursor).trimEnd();
|
|
1550
|
+
const afterNewline = commentMasked
|
|
1551
|
+
.slice(character === "\r" && commentMasked[cursor + 1] === "\n" ? cursor + 2 : cursor + 1)
|
|
1552
|
+
.trimStart();
|
|
1553
|
+
const continuation = before.endsWith("+") ||
|
|
1554
|
+
before.endsWith("=") ||
|
|
1555
|
+
afterNewline.startsWith("+") ||
|
|
1556
|
+
nesting > 0 ||
|
|
1557
|
+
quote === "`";
|
|
1558
|
+
if (valueSeenOnLine && !continuation) {
|
|
1559
|
+
break;
|
|
1560
|
+
}
|
|
1561
|
+
if (character === "\r" && commentMasked[cursor + 1] === "\n") {
|
|
1562
|
+
cursor++;
|
|
1563
|
+
}
|
|
1564
|
+
lineCount++;
|
|
1565
|
+
valueSeenOnLine = false;
|
|
1566
|
+
cursor++;
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
if (!/\s/.test(character)) {
|
|
1570
|
+
valueSeenOnLine = true;
|
|
1571
|
+
}
|
|
1572
|
+
cursor++;
|
|
1573
|
+
}
|
|
1574
|
+
return {
|
|
1575
|
+
content: text.slice(valueStart, cursor),
|
|
1576
|
+
exhausted: cursor < text.length &&
|
|
1577
|
+
(cursor - valueStart >= 32_768 || lineCount >= 64),
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
function maskJsCommentTrivia(text) {
|
|
1581
|
+
const output = text.split("");
|
|
1582
|
+
let quote;
|
|
1583
|
+
let escaped = false;
|
|
1584
|
+
for (let index = 0; index < text.length; index++) {
|
|
1585
|
+
const character = text[index] ?? "";
|
|
1586
|
+
if (quote != null) {
|
|
1587
|
+
if (!escaped && character === quote) {
|
|
1588
|
+
quote = undefined;
|
|
1589
|
+
}
|
|
1590
|
+
escaped = !escaped && character === "\\";
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
1594
|
+
quote = character;
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
if (character === "/" && text[index + 1] === "*") {
|
|
1598
|
+
output[index] = " ";
|
|
1599
|
+
output[index + 1] = " ";
|
|
1600
|
+
index += 2;
|
|
1601
|
+
while (index < text.length) {
|
|
1602
|
+
if (text[index] === "*" && text[index + 1] === "/") {
|
|
1603
|
+
output[index] = " ";
|
|
1604
|
+
output[index + 1] = " ";
|
|
1605
|
+
index++;
|
|
1606
|
+
break;
|
|
1607
|
+
}
|
|
1608
|
+
if (text[index] !== "\r" && text[index] !== "\n") {
|
|
1609
|
+
output[index] = " ";
|
|
1610
|
+
}
|
|
1611
|
+
index++;
|
|
1612
|
+
}
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
if (character === "/" && text[index + 1] === "/") {
|
|
1616
|
+
output[index] = " ";
|
|
1617
|
+
output[index + 1] = " ";
|
|
1618
|
+
index += 2;
|
|
1619
|
+
while (index < text.length && text[index] !== "\r" && text[index] !== "\n") {
|
|
1620
|
+
output[index] = " ";
|
|
1621
|
+
index++;
|
|
1622
|
+
}
|
|
1623
|
+
index--;
|
|
1624
|
+
}
|
|
196
1625
|
}
|
|
197
|
-
return
|
|
1626
|
+
return output.join("");
|
|
198
1627
|
}
|
|
199
1628
|
function shannonEntropy(value) {
|
|
200
1629
|
const counts = new Map();
|