@provos/ironcurtain 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -8
- package/dist/auth/oauth-provider.js +1 -4
- package/dist/auth/oauth-provider.js.map +1 -1
- package/dist/cli.js +38 -0
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +162 -7
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/first-start.js +61 -0
- package/dist/config/first-start.js.map +1 -1
- package/dist/config/generated/compiled-policy.json +528 -161
- package/dist/config/generated/test-scenarios.json +4810 -170
- package/dist/config/generated/tool-annotations.json +34 -47
- package/dist/config/index.d.ts +58 -0
- package/dist/config/index.js +97 -15
- package/dist/config/index.js.map +1 -1
- package/dist/config/mcp-servers.json +2 -2
- package/dist/config/paths.d.ts +175 -0
- package/dist/config/paths.js +308 -14
- package/dist/config/paths.js.map +1 -1
- package/dist/config/types.d.ts +7 -1
- package/dist/config/user-config.d.ts +53 -5
- package/dist/config/user-config.js +96 -30
- package/dist/config/user-config.js.map +1 -1
- package/dist/config/validate-policy-dir.d.ts +10 -0
- package/dist/config/validate-policy-dir.js +59 -0
- package/dist/config/validate-policy-dir.js.map +1 -0
- package/dist/cron/job-commands.js +22 -2
- package/dist/cron/job-commands.js.map +1 -1
- package/dist/cron/types.d.ts +16 -2
- package/dist/cron/types.js +1 -3
- package/dist/cron/types.js.map +1 -1
- package/dist/daemon/daemon-command.js +16 -2
- package/dist/daemon/daemon-command.js.map +1 -1
- package/dist/daemon/ironcurtain-daemon.d.ts +44 -1
- package/dist/daemon/ironcurtain-daemon.js +210 -8
- package/dist/daemon/ironcurtain-daemon.js.map +1 -1
- package/dist/docker/adapters/claude-code.js +194 -20
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/adapters/goose.d.ts +12 -11
- package/dist/docker/adapters/goose.js +80 -27
- package/dist/docker/adapters/goose.js.map +1 -1
- package/dist/docker/agent-adapter.d.ts +178 -4
- package/dist/docker/agent-adapter.js +14 -0
- package/dist/docker/agent-adapter.js.map +1 -1
- package/dist/docker/code-mode-proxy.d.ts +27 -0
- package/dist/docker/code-mode-proxy.js +3 -0
- package/dist/docker/code-mode-proxy.js.map +1 -1
- package/dist/docker/docker-agent-session.d.ts +113 -62
- package/dist/docker/docker-agent-session.js +242 -294
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +246 -21
- package/dist/docker/docker-infrastructure.js +520 -29
- package/dist/docker/docker-infrastructure.js.map +1 -1
- package/dist/docker/docker-manager.d.ts +36 -1
- package/dist/docker/docker-manager.js +135 -30
- package/dist/docker/docker-manager.js.map +1 -1
- package/dist/docker/docker-progress-sink.d.ts +43 -0
- package/dist/docker/docker-progress-sink.js +299 -0
- package/dist/docker/docker-progress-sink.js.map +1 -0
- package/dist/docker/mitm-proxy.d.ts +84 -1
- package/dist/docker/mitm-proxy.js +380 -18
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +46 -3
- package/dist/docker/oauth-credentials.js +56 -14
- package/dist/docker/oauth-credentials.js.map +1 -1
- package/dist/docker/oauth-token-manager.js +2 -2
- package/dist/docker/oauth-token-manager.js.map +1 -1
- package/dist/docker/orientation.d.ts +1 -1
- package/dist/docker/provider-config.d.ts +49 -2
- package/dist/docker/provider-config.js +202 -28
- package/dist/docker/provider-config.js.map +1 -1
- package/dist/docker/proxy-tools.d.ts +0 -2
- package/dist/docker/proxy-tools.js +2 -7
- package/dist/docker/proxy-tools.js.map +1 -1
- package/dist/docker/pty-session.d.ts +49 -1
- package/dist/docker/pty-session.js +199 -69
- package/dist/docker/pty-session.js.map +1 -1
- package/dist/docker/pty-types.d.ts +0 -2
- package/dist/docker/pty-types.js +0 -2
- package/dist/docker/pty-types.js.map +1 -1
- package/dist/docker/resource-limits.d.ts +125 -0
- package/dist/docker/resource-limits.js +200 -0
- package/dist/docker/resource-limits.js.map +1 -0
- package/dist/docker/spawn-with-idle-timeout.d.ts +39 -0
- package/dist/docker/spawn-with-idle-timeout.js +152 -0
- package/dist/docker/spawn-with-idle-timeout.js.map +1 -0
- package/dist/docker/sse-extractor.d.ts +51 -0
- package/dist/docker/sse-extractor.js +303 -0
- package/dist/docker/sse-extractor.js.map +1 -0
- package/dist/docker/token-stream-bus.d.ts +83 -0
- package/dist/docker/token-stream-bus.js +98 -0
- package/dist/docker/token-stream-bus.js.map +1 -0
- package/dist/docker/token-stream-types.d.ts +71 -0
- package/dist/docker/token-stream-types.js +8 -0
- package/dist/docker/token-stream-types.js.map +1 -0
- package/dist/docker/types.d.ts +53 -6
- package/dist/doctor/checks.d.ts +138 -0
- package/dist/doctor/checks.js +511 -0
- package/dist/doctor/checks.js.map +1 -0
- package/dist/doctor/doctor-command.d.ts +29 -0
- package/dist/doctor/doctor-command.js +160 -0
- package/dist/doctor/doctor-command.js.map +1 -0
- package/dist/doctor/mcp-liveness.d.ts +28 -0
- package/dist/doctor/mcp-liveness.js +84 -0
- package/dist/doctor/mcp-liveness.js.map +1 -0
- package/dist/doctor/oauth-checks.d.ts +23 -0
- package/dist/doctor/oauth-checks.js +157 -0
- package/dist/doctor/oauth-checks.js.map +1 -0
- package/dist/doctor/output.d.ts +26 -0
- package/dist/doctor/output.js +82 -0
- package/dist/doctor/output.js.map +1 -0
- package/dist/escalation/listener-command.js +0 -3
- package/dist/escalation/listener-command.js.map +1 -1
- package/dist/event-bus/typed-event-bus.d.ts +26 -0
- package/dist/event-bus/typed-event-bus.js +35 -0
- package/dist/event-bus/typed-event-bus.js.map +1 -0
- package/dist/index.js +24 -9
- package/dist/index.js.map +1 -1
- package/dist/logger.js +31 -2
- package/dist/logger.js.map +1 -1
- package/dist/memory/auto-save.d.ts +18 -4
- package/dist/memory/auto-save.js +46 -6
- package/dist/memory/auto-save.js.map +1 -1
- package/dist/memory/memory-policy.d.ts +41 -0
- package/dist/memory/memory-policy.js +36 -0
- package/dist/memory/memory-policy.js.map +1 -0
- package/dist/mux/mux-command.d.ts +10 -1
- package/dist/mux/mux-command.js +56 -35
- package/dist/mux/mux-command.js.map +1 -1
- package/dist/mux/mux-renderer.js +2 -12
- package/dist/mux/mux-renderer.js.map +1 -1
- package/dist/mux/session-scanner.js +1 -2
- package/dist/mux/session-scanner.js.map +1 -1
- package/dist/{pipeline → observability}/llm-logger.d.ts +8 -3
- package/dist/{pipeline → observability}/llm-logger.js +8 -3
- package/dist/observability/llm-logger.js.map +1 -0
- package/dist/observe/observe-command.d.ts +12 -0
- package/dist/observe/observe-command.js +298 -0
- package/dist/observe/observe-command.js.map +1 -0
- package/dist/observe/observe-renderer.d.ts +24 -0
- package/dist/observe/observe-renderer.js +87 -0
- package/dist/observe/observe-renderer.js.map +1 -0
- package/dist/observe/observe-tui-rain.d.ts +42 -0
- package/dist/observe/observe-tui-rain.js +531 -0
- package/dist/observe/observe-tui-rain.js.map +1 -0
- package/dist/observe/observe-tui-text-panel.d.ts +46 -0
- package/dist/observe/observe-tui-text-panel.js +525 -0
- package/dist/observe/observe-tui-text-panel.js.map +1 -0
- package/dist/observe/observe-tui-types.d.ts +270 -0
- package/dist/observe/observe-tui-types.js +200 -0
- package/dist/observe/observe-tui-types.js.map +1 -0
- package/dist/observe/observe-tui-word-scorer.d.ts +143 -0
- package/dist/observe/observe-tui-word-scorer.js +572 -0
- package/dist/observe/observe-tui-word-scorer.js.map +1 -0
- package/dist/observe/observe-tui.d.ts +59 -0
- package/dist/observe/observe-tui.js +592 -0
- package/dist/observe/observe-tui.js.map +1 -0
- package/dist/persona/memory-gate.d.ts +19 -0
- package/dist/persona/memory-gate.js +33 -0
- package/dist/persona/memory-gate.js.map +1 -0
- package/dist/persona/persona-command.js +41 -6
- package/dist/persona/persona-command.js.map +1 -1
- package/dist/persona/resolve.d.ts +22 -0
- package/dist/persona/resolve.js +45 -2
- package/dist/persona/resolve.js.map +1 -1
- package/dist/persona/types.d.ts +16 -0
- package/dist/pipeline/constitution-compiler.d.ts +25 -14
- package/dist/pipeline/constitution-compiler.js +107 -38
- package/dist/pipeline/constitution-compiler.js.map +1 -1
- package/dist/pipeline/dynamic-list-types.d.ts +3 -6
- package/dist/pipeline/dynamic-list-types.js +3 -16
- package/dist/pipeline/dynamic-list-types.js.map +1 -1
- package/dist/pipeline/pipeline-runner.js +89 -50
- package/dist/pipeline/pipeline-runner.js.map +1 -1
- package/dist/pipeline/pipeline-shared.d.ts +1 -1
- package/dist/pipeline/pipeline-shared.js +3 -11
- package/dist/pipeline/pipeline-shared.js.map +1 -1
- package/dist/pipeline/policy-verifier.js +16 -3
- package/dist/pipeline/policy-verifier.js.map +1 -1
- package/dist/pipeline/proxy-mcp-connections.js +17 -2
- package/dist/pipeline/proxy-mcp-connections.js.map +1 -1
- package/dist/pipeline/scenario-generator.d.ts +4 -8
- package/dist/pipeline/scenario-generator.js +13 -46
- package/dist/pipeline/scenario-generator.js.map +1 -1
- package/dist/sandbox/index.d.ts +14 -0
- package/dist/sandbox/index.js +335 -116
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/ironcurtain-protocol.d.ts +52 -0
- package/dist/sandbox/ironcurtain-protocol.js +185 -0
- package/dist/sandbox/ironcurtain-protocol.js.map +1 -0
- package/dist/session/agent-session.d.ts +6 -0
- package/dist/session/agent-session.js +16 -5
- package/dist/session/agent-session.js.map +1 -1
- package/dist/session/cli-transport.js +0 -6
- package/dist/session/cli-transport.js.map +1 -1
- package/dist/session/index.d.ts +35 -14
- package/dist/session/index.js +324 -130
- package/dist/session/index.js.map +1 -1
- package/dist/session/preflight.d.ts +50 -13
- package/dist/session/preflight.js +252 -80
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/prompts.d.ts +2 -5
- package/dist/session/prompts.js.map +1 -1
- package/dist/session/session-manager.d.ts +61 -6
- package/dist/session/session-manager.js +72 -10
- package/dist/session/session-manager.js.map +1 -1
- package/dist/session/session-metadata.d.ts +21 -5
- package/dist/session/session-metadata.js +34 -12
- package/dist/session/session-metadata.js.map +1 -1
- package/dist/session/types.d.ts +311 -5
- package/dist/session/types.js +57 -1
- package/dist/session/types.js.map +1 -1
- package/dist/signal/setup-signal.js +13 -21
- package/dist/signal/setup-signal.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +16 -1
- package/dist/signal/signal-bot-daemon.js +39 -27
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-container.js +7 -1
- package/dist/signal/signal-container.js.map +1 -1
- package/dist/skills/discovery.d.ts +77 -0
- package/dist/skills/discovery.js +206 -0
- package/dist/skills/discovery.js.map +1 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +3 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/staging.d.ts +28 -0
- package/dist/skills/staging.js +68 -0
- package/dist/skills/staging.js.map +1 -0
- package/dist/skills/types.d.ts +46 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/trusted-process/approval-whitelist.d.ts +0 -2
- package/dist/trusted-process/approval-whitelist.js +17 -17
- package/dist/trusted-process/approval-whitelist.js.map +1 -1
- package/dist/trusted-process/async-mutex.d.ts +32 -0
- package/dist/trusted-process/async-mutex.js +50 -0
- package/dist/trusted-process/async-mutex.js.map +1 -0
- package/dist/trusted-process/audit-log.d.ts +38 -1
- package/dist/trusted-process/audit-log.js +82 -13
- package/dist/trusted-process/audit-log.js.map +1 -1
- package/dist/trusted-process/call-circuit-breaker.js +2 -1
- package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
- package/dist/trusted-process/control-server.d.ts +101 -0
- package/dist/trusted-process/control-server.js +282 -0
- package/dist/trusted-process/control-server.js.map +1 -0
- package/dist/trusted-process/domain-utils.d.ts +6 -6
- package/dist/trusted-process/domain-utils.js +7 -23
- package/dist/trusted-process/domain-utils.js.map +1 -1
- package/dist/trusted-process/error-prefixes.d.ts +16 -0
- package/dist/trusted-process/error-prefixes.js +17 -0
- package/dist/trusted-process/error-prefixes.js.map +1 -0
- package/dist/trusted-process/index.d.ts +40 -23
- package/dist/trusted-process/index.js +131 -225
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/list-matcher.d.ts +17 -0
- package/dist/trusted-process/list-matcher.js +31 -0
- package/dist/trusted-process/list-matcher.js.map +1 -0
- package/dist/trusted-process/mcp-client-manager.d.ts +35 -6
- package/dist/trusted-process/mcp-client-manager.js +40 -45
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-proxy-server.d.ts +31 -120
- package/dist/trusted-process/mcp-proxy-server.js +166 -657
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/policy-engine.d.ts +16 -4
- package/dist/trusted-process/policy-engine.js +90 -45
- package/dist/trusted-process/policy-engine.js.map +1 -1
- package/dist/trusted-process/policy-roots.d.ts +6 -0
- package/dist/trusted-process/policy-roots.js +20 -0
- package/dist/trusted-process/policy-roots.js.map +1 -1
- package/dist/trusted-process/sandbox-integration.d.ts +12 -0
- package/dist/trusted-process/sandbox-integration.js +18 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -1
- package/dist/trusted-process/tool-call-coordinator.d.ts +269 -0
- package/dist/trusted-process/tool-call-coordinator.js +508 -0
- package/dist/trusted-process/tool-call-coordinator.js.map +1 -0
- package/dist/trusted-process/tool-call-pipeline.d.ts +205 -0
- package/dist/trusted-process/tool-call-pipeline.js +798 -0
- package/dist/trusted-process/tool-call-pipeline.js.map +1 -0
- package/dist/trusted-process/tool-description-hints.d.ts +1 -1
- package/dist/types/argument-roles.d.ts +1 -1
- package/dist/types/argument-roles.js +14 -1
- package/dist/types/argument-roles.js.map +1 -1
- package/dist/types/audit.d.ts +9 -0
- package/dist/{session → types}/errors.d.ts +8 -2
- package/dist/{session → types}/errors.js +8 -2
- package/dist/types/errors.js.map +1 -0
- package/dist/types/reserved-servers.d.ts +11 -0
- package/dist/types/reserved-servers.js +12 -0
- package/dist/types/reserved-servers.js.map +1 -0
- package/dist/types/server-listing.d.ts +14 -0
- package/dist/types/server-listing.js +2 -0
- package/dist/types/server-listing.js.map +1 -0
- package/dist/types/slug.d.ts +2 -0
- package/dist/types/slug.js +2 -0
- package/dist/types/slug.js.map +1 -1
- package/dist/types/url-normalize.d.ts +16 -0
- package/dist/types/url-normalize.js +33 -0
- package/dist/types/url-normalize.js.map +1 -0
- package/dist/utils/error-message.d.ts +8 -0
- package/dist/utils/error-message.js +11 -0
- package/dist/utils/error-message.js.map +1 -0
- package/dist/utils/exec-error.d.ts +19 -0
- package/dist/utils/exec-error.js +16 -0
- package/dist/utils/exec-error.js.map +1 -0
- package/dist/utils/is-plain-object.d.ts +7 -0
- package/dist/utils/is-plain-object.js +12 -0
- package/dist/utils/is-plain-object.js.map +1 -0
- package/dist/utils/preflight-checks.d.ts +16 -0
- package/dist/utils/preflight-checks.js +178 -0
- package/dist/utils/preflight-checks.js.map +1 -0
- package/dist/web-ui/__tests__/json-rpc-dispatch.test.d.ts +7 -0
- package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +725 -0
- package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -0
- package/dist/web-ui/dispatch/escalation-dispatch.d.ts +7 -0
- package/dist/web-ui/dispatch/escalation-dispatch.js +52 -0
- package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/job-dispatch.d.ts +8 -0
- package/dist/web-ui/dispatch/job-dispatch.js +83 -0
- package/dist/web-ui/dispatch/job-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/persona-dispatch.d.ts +6 -0
- package/dist/web-ui/dispatch/persona-dispatch.js +116 -0
- package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/session-dispatch.d.ts +9 -0
- package/dist/web-ui/dispatch/session-dispatch.js +201 -0
- package/dist/web-ui/dispatch/session-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/token-stream-dispatch.d.ts +15 -0
- package/dist/web-ui/dispatch/token-stream-dispatch.js +55 -0
- package/dist/web-ui/dispatch/token-stream-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/types.d.ts +31 -0
- package/dist/web-ui/dispatch/types.js +68 -0
- package/dist/web-ui/dispatch/types.js.map +1 -0
- package/dist/web-ui/dispatch/workflow-dispatch.d.ts +103 -0
- package/dist/web-ui/dispatch/workflow-dispatch.js +925 -0
- package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -0
- package/dist/web-ui/json-rpc-dispatch.d.ts +12 -0
- package/dist/web-ui/json-rpc-dispatch.js +34 -0
- package/dist/web-ui/json-rpc-dispatch.js.map +1 -0
- package/dist/web-ui/state-graph.d.ts +13 -0
- package/dist/web-ui/state-graph.js +82 -0
- package/dist/web-ui/state-graph.js.map +1 -0
- package/dist/web-ui/token-stream-bridge.d.ts +76 -0
- package/dist/web-ui/token-stream-bridge.js +265 -0
- package/dist/web-ui/token-stream-bridge.js.map +1 -0
- package/dist/web-ui/web-event-bus.d.ts +152 -0
- package/dist/web-ui/web-event-bus.js +19 -0
- package/dist/web-ui/web-event-bus.js.map +1 -0
- package/dist/web-ui/web-session-transport.d.ts +31 -0
- package/dist/web-ui/web-session-transport.js +109 -0
- package/dist/web-ui/web-session-transport.js.map +1 -0
- package/dist/web-ui/web-ui-server.d.ts +76 -0
- package/dist/web-ui/web-ui-server.js +461 -0
- package/dist/web-ui/web-ui-server.js.map +1 -0
- package/dist/web-ui/web-ui-types.d.ts +290 -0
- package/dist/web-ui/web-ui-types.js +32 -0
- package/dist/web-ui/web-ui-types.js.map +1 -0
- package/dist/web-ui/ws-utils.d.ts +5 -0
- package/dist/web-ui/ws-utils.js +12 -0
- package/dist/web-ui/ws-utils.js.map +1 -0
- package/dist/web-ui-static/assets/index-BFO5z1js.js +76 -0
- package/dist/web-ui-static/assets/index-CCUwrkIL.css +1 -0
- package/dist/web-ui-static/assets/orbitron-latin-700-normal-4jsRXGGJ.woff2 +0 -0
- package/dist/web-ui-static/assets/orbitron-latin-700-normal-DmYOh-W9.woff +0 -0
- package/dist/web-ui-static/index.html +14 -0
- package/dist/workflow/artifacts.d.ts +59 -0
- package/dist/workflow/artifacts.js +188 -0
- package/dist/workflow/artifacts.js.map +1 -0
- package/dist/workflow/checkpoint-selection.d.ts +34 -0
- package/dist/workflow/checkpoint-selection.js +48 -0
- package/dist/workflow/checkpoint-selection.js.map +1 -0
- package/dist/workflow/checkpoint.d.ts +35 -0
- package/dist/workflow/checkpoint.js +84 -0
- package/dist/workflow/checkpoint.js.map +1 -0
- package/dist/workflow/cli-support.d.ts +92 -0
- package/dist/workflow/cli-support.js +423 -0
- package/dist/workflow/cli-support.js.map +1 -0
- package/dist/workflow/definition-loader.d.ts +32 -0
- package/dist/workflow/definition-loader.js +56 -0
- package/dist/workflow/definition-loader.js.map +1 -0
- package/dist/workflow/discovery.d.ts +78 -0
- package/dist/workflow/discovery.js +203 -0
- package/dist/workflow/discovery.js.map +1 -0
- package/dist/workflow/errors.d.ts +66 -0
- package/dist/workflow/errors.js +49 -0
- package/dist/workflow/errors.js.map +1 -0
- package/dist/workflow/guards.d.ts +8 -0
- package/dist/workflow/guards.js +38 -0
- package/dist/workflow/guards.js.map +1 -0
- package/dist/workflow/lint-integration.d.ts +64 -0
- package/dist/workflow/lint-integration.js +56 -0
- package/dist/workflow/lint-integration.js.map +1 -0
- package/dist/workflow/lint.d.ts +66 -0
- package/dist/workflow/lint.js +410 -0
- package/dist/workflow/lint.js.map +1 -0
- package/dist/workflow/machine-builder.d.ts +90 -0
- package/dist/workflow/machine-builder.js +485 -0
- package/dist/workflow/machine-builder.js.map +1 -0
- package/dist/workflow/message-log.d.ts +85 -0
- package/dist/workflow/message-log.js +40 -0
- package/dist/workflow/message-log.js.map +1 -0
- package/dist/workflow/orchestrator.d.ts +450 -0
- package/dist/workflow/orchestrator.js +1941 -0
- package/dist/workflow/orchestrator.js.map +1 -0
- package/dist/workflow/prompt-builder.d.ts +39 -0
- package/dist/workflow/prompt-builder.js +242 -0
- package/dist/workflow/prompt-builder.js.map +1 -0
- package/dist/workflow/run-state-command.d.ts +9 -0
- package/dist/workflow/run-state-command.js +414 -0
- package/dist/workflow/run-state-command.js.map +1 -0
- package/dist/workflow/run-state-debug-capture.d.ts +40 -0
- package/dist/workflow/run-state-debug-capture.js +101 -0
- package/dist/workflow/run-state-debug-capture.js.map +1 -0
- package/dist/workflow/status-parser.d.ts +68 -0
- package/dist/workflow/status-parser.js +259 -0
- package/dist/workflow/status-parser.js.map +1 -0
- package/dist/workflow/types.d.ts +498 -0
- package/dist/workflow/types.js +83 -0
- package/dist/workflow/types.js.map +1 -0
- package/dist/workflow/validate.d.ts +58 -0
- package/dist/workflow/validate.js +499 -0
- package/dist/workflow/validate.js.map +1 -0
- package/dist/workflow/workflow-command.d.ts +26 -0
- package/dist/workflow/workflow-command.js +648 -0
- package/dist/workflow/workflow-command.js.map +1 -0
- package/dist/workflow/workflow-discovery.d.ts +25 -0
- package/dist/workflow/workflow-discovery.js +100 -0
- package/dist/workflow/workflow-discovery.js.map +1 -0
- package/dist/workflow/workflow-manager.d.ts +122 -0
- package/dist/workflow/workflow-manager.js +308 -0
- package/dist/workflow/workflow-manager.js.map +1 -0
- package/dist/workflow/workflows/design-and-code/workflow.yaml +169 -0
- package/dist/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
- package/dist/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
- package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
- package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
- package/dist/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
- package/dist/workflow/workflows/test-email-summary/workflow.yaml +89 -0
- package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +201 -0
- package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +166 -0
- package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +185 -0
- package/dist/workflow/workflows/vuln-discovery/workflow.yaml +920 -0
- package/dist/workflow/worktree.d.ts +43 -0
- package/dist/workflow/worktree.js +106 -0
- package/dist/workflow/worktree.js.map +1 -0
- package/docker/Dockerfile.base +9 -0
- package/docker/Dockerfile.base.arm64 +9 -0
- package/docker/entrypoint-claude-code.sh +46 -0
- package/docker/entrypoint-goose.sh +25 -0
- package/package.json +19 -8
- package/packages/web-ui/README.md +113 -0
- package/packages/web-ui/docs/workflow-state-machine.png +0 -0
- package/src/config/generated/compiled-policy.json +528 -161
- package/src/config/generated/test-scenarios.json +4810 -170
- package/src/config/generated/tool-annotations.json +34 -47
- package/src/config/mcp-servers.json +2 -2
- package/src/workflow/workflows/design-and-code/workflow.yaml +169 -0
- package/src/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
- package/src/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
- package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
- package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
- package/src/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
- package/src/workflow/workflows/test-email-summary/workflow.yaml +89 -0
- package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +201 -0
- package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +166 -0
- package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +185 -0
- package/src/workflow/workflows/vuln-discovery/workflow.yaml +920 -0
- package/dist/pipeline/llm-logger.js.map +0 -1
- package/dist/session/errors.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: harness-design-fuzzing
|
|
3
|
+
description: Reference vocabulary for designing instrumented harnesses that drive vulnerability discovery — design classes (trigger-driven vs coverage-driven), tiered scope (T1 isolated function / T2 multi-component / T3 full build), systematic input exploration, the two-coverage distinction (fuzzer-feedback vs audit), existing-fuzzer selection (libFuzzer / AFL++ / Jazzer / atheris / `go test -fuzz`), seed-corpus discipline, diagnostic checkpoints, common pitfalls, and design-document scope. Read when designing or reviewing a harness specification. Stays neutral on language and stack — pulls in the relevant surface skill (e.g. `memory-safety-c-cpp`) for bug-class taxonomy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Harness Design for Vulnerability Discovery
|
|
7
|
+
|
|
8
|
+
Reference vocabulary for designing instrumented harnesses that drive vulnerability discovery. Catalogs the classes, scopes, instrumentation choices, and pitfalls a harness design needs to reason about.
|
|
9
|
+
|
|
10
|
+
A harness is not a unit test. The point of a harness is to **systematically explore an input space against an oracle that fires on a violation** — not to confirm a few hand-picked cases. Hand-picked scenarios miss boundary values; the boundary is where the bug lives.
|
|
11
|
+
|
|
12
|
+
## Design class — pick first
|
|
13
|
+
|
|
14
|
+
Every harness has exactly one of two design classes. The class drives the sweep variables and the oracle. Tier (below) is orthogonal — any tier can be either class.
|
|
15
|
+
|
|
16
|
+
- **Trigger-driven.** The directive supplies a falsifiable claim with a named violation site — a specific function, value range, and expected oracle (a bounds check fires, a type narrows lossily, a sentinel collides, a state-machine transition is reached out of order). The harness sweeps the **hypothesis input variables**. The oracle is the named violation pattern firing.
|
|
17
|
+
|
|
18
|
+
- **Coverage-driven.** The directive supplies an under-exercised dispatch surface — a code region the project's existing fuzzers don't reach, with named dispatch axes (option flags, message types, opcode tables, mode bits, format variants) the input space hasn't crossed. The harness sweeps the **dispatch axes**. The oracle is **any sanitizer error within the named region**. The named region must be a concrete file/function set, not "somewhere in the target."
|
|
19
|
+
|
|
20
|
+
Pick coverage-driven when prior trigger-driven rounds against the same region have been mitigated by upstream guards but the region itself is untested by the existing fuzz infrastructure. Pick trigger-driven when there is a specific theory to falsify.
|
|
21
|
+
|
|
22
|
+
## Harness tiers
|
|
23
|
+
|
|
24
|
+
Three tiers of infrastructure scope. Match tier to hypothesis scope; never use Tier 1 for a cross-component target.
|
|
25
|
+
|
|
26
|
+
### Tier 1 — Isolated function test
|
|
27
|
+
Extract the relevant function(s) into a standalone, self-contained program. Copy the exact types, macros, and helper functions verbatim from the source. Stub only I/O, networking, and allocation.
|
|
28
|
+
|
|
29
|
+
**Critical:** the test must faithfully preserve ALL code paths that interact with the violation site. If the hypothesis involves a value computed in function A being consumed in function B, BOTH functions must be included — do not test A in isolation and assume B's behavior.
|
|
30
|
+
|
|
31
|
+
Use Tier 1 when the hypothesis is about a single function: arithmetic boundary, type narrowing, off-by-one, a self-contained parsing routine. Runs millions of trials per second.
|
|
32
|
+
|
|
33
|
+
### Tier 2 — Multi-component harness
|
|
34
|
+
Link multiple real source files from the project. Preserve actual data structures, type definitions, inter-function calls, and state that accumulates across calls. Specify:
|
|
35
|
+
|
|
36
|
+
- Which source files to link
|
|
37
|
+
- Which functions' logic must be preserved vs stubbed
|
|
38
|
+
- Which struct layouts must match the real project
|
|
39
|
+
- Coverage instrumentation for both kinds (see below)
|
|
40
|
+
|
|
41
|
+
Use Tier 2 when the hypothesis involves cross-function interaction — a value computed in one function being consumed or compared in another, sentinel collisions across components, state accumulated across multiple call sites, or dispatch tables linked across compilation units.
|
|
42
|
+
|
|
43
|
+
### Tier 3 — Full build with instrumented input
|
|
44
|
+
Compile the actual project (or a substantial subset) with sanitizers and coverage. A driver feeds crafted input files, protocol messages, or CLI invocations through real entry points. Specify:
|
|
45
|
+
|
|
46
|
+
- The protocol/interaction sequence the driver must follow
|
|
47
|
+
- Required setup (handshakes, session establishment, state-machine transitions)
|
|
48
|
+
- Session reset mechanism between runs
|
|
49
|
+
- Coverage report format (lcov, JSON, or stack-equivalent)
|
|
50
|
+
|
|
51
|
+
Use Tier 3 when the bug depends on initialization sequences, global state, runtime configuration, or protocol-level framing that can't be faithfully stubbed.
|
|
52
|
+
|
|
53
|
+
### Picking the right tier
|
|
54
|
+
|
|
55
|
+
- Hypothesis names ONE function → Tier 1.
|
|
56
|
+
- Hypothesis involves a value flowing between 2+ functions, or a sentinel/type used across components → Tier 2 minimum.
|
|
57
|
+
- Hypothesis requires real initialization, protocol framing, or global state → Tier 3.
|
|
58
|
+
- If in doubt, go higher. Tier-1 reproductions of cross-component bugs produce false negatives and false positives — both equally misleading.
|
|
59
|
+
|
|
60
|
+
## Systematic input exploration
|
|
61
|
+
|
|
62
|
+
The harness must FUZZ, not unit-test. For each swept variable (hypothesis inputs for trigger-driven, dispatch axes for coverage-driven), the design must specify:
|
|
63
|
+
|
|
64
|
+
- **Sweep range.** The full range of values to iterate over. Cover the entire representable range of the type, not just "a few specific values." For wide ranges, specify dense sampling near type boundaries (min, max, zero, `2^N - 1` for each relevant N, sign-flip points) plus sparse sampling elsewhere. For coverage-driven dispatch axes, the range is the full set of dispatch values plus their realistic combinations.
|
|
65
|
+
- **Search strategy.** Exhaustive enumeration if the space is < 100M combinations; boundary-dense + random sampling otherwise; coverage-guided fuzzer for large or structured input spaces.
|
|
66
|
+
- **Observables.** At each input value, what does the harness record? Stored value in the target buffer, result of the bounds check at `<file>:<line>`, return value of the target function, sanitizer error, canary state, dispatch arm hit. Be specific by site.
|
|
67
|
+
- **Positive-finding condition.** What pattern in the observables confirms the design's claim? For trigger-driven: be specific — not "a crash happens" but "the bounds check at `<file>:<line>` evaluates true when the input value exceeds the buffer's allocated size." For coverage-driven: "any sanitizer error within the named code region" is acceptable, but the region must be concrete.
|
|
68
|
+
|
|
69
|
+
The design specifies input variables and ranges. The implementer drives the sweep. Do NOT prescribe individual "Test A / Test B / Test C" scenarios with fixed values — that's unit testing and will miss the actual boundary.
|
|
70
|
+
|
|
71
|
+
### Multi-hypothesis bundling
|
|
72
|
+
|
|
73
|
+
A trigger-driven design may scope a single hypothesis or a related set when they share input space and code region. Bundle when consolidating doesn't dilute the sweep (e.g., three integer-overflow hypotheses on the same allocator → one harness). Keep separate when input distributions conflict (one wants huge dimensions, another wants small) or observation points differ enough that one harness can't see both.
|
|
74
|
+
|
|
75
|
+
When bundling, the sweep range is the **union** of input-variable ranges, the positive-finding condition is the **disjunction** of named violation patterns, and observables must cover all named sites. Briefly justify the bundling — bundled designs only pay off when the hypotheses share enough input space and code region that one harness can exercise them coherently. More than three hypotheses bundled in one harness is a signal of an unfocused design.
|
|
76
|
+
|
|
77
|
+
## Coverage instrumentation — two distinct concerns
|
|
78
|
+
|
|
79
|
+
Fuzzing requires two instrumentation decisions that are easy to confuse. The design must address both.
|
|
80
|
+
|
|
81
|
+
### 1. Fuzzer-feedback coverage
|
|
82
|
+
|
|
83
|
+
Instrumentation the fuzzer consumes at runtime to guide mutation. Tool-specific, falls into three categories:
|
|
84
|
+
|
|
85
|
+
- **Compile-time sanitizer coverage.** libFuzzer's `-fsanitize=fuzzer` (which enables `-fsanitize-coverage=trace-cmp` by default), often combined with `-fsanitize=address,undefined`. Additional coverage knobs: `trace-pc-guard`, `trace-cmp`, `trace-div`, `trace-gep`.
|
|
86
|
+
- **Compile wrappers.** AFL++'s `afl-clang-fast`, `afl-clang-lto` (LTO mode, generally preferred when usable), or `afl-gcc-fast`.
|
|
87
|
+
- **Runtime agents** for managed stacks. Jazzer's Java agent for JVM, atheris's import rewriter for Python, Go's built-in coverage for `go test -fuzz`.
|
|
88
|
+
|
|
89
|
+
The design must name **the exact mechanism** AND **the exact metric field name the validator will read** from the fuzzer's status output. Without the metric name, downstream verification cannot gate on "the fuzzer's feedback chain reached the target." Canonical field names:
|
|
90
|
+
|
|
91
|
+
| Fuzzer | Field for "edges/blocks hit by feedback" |
|
|
92
|
+
| ----------------- | ---------------------------------------- |
|
|
93
|
+
| libFuzzer | `cov:` (also `ft:` for features) |
|
|
94
|
+
| AFL++ | `edges_found` |
|
|
95
|
+
| Jazzer | reports via libFuzzer (`cov:`) |
|
|
96
|
+
| Go `go test -fuzz`| `new interesting` count |
|
|
97
|
+
| atheris | reports via libFuzzer (`cov:`) |
|
|
98
|
+
|
|
99
|
+
### 2. Audit coverage
|
|
100
|
+
|
|
101
|
+
Post-run reporting of which lines executed: `llvm-cov`, `gcov`, Python `coverage.py`, JaCoCo, Go's `-cover`, and stack-equivalents. This is an audit tool, not a feedback signal. A harness can have audit coverage without fuzzer-feedback coverage — that is the **forgot-to-instrument-the-target pitfall**: the run reports lines hit in the wrapper but the fuzzer was driving randomly because no feedback signal reached it.
|
|
102
|
+
|
|
103
|
+
### Target-code instrumentation (required)
|
|
104
|
+
|
|
105
|
+
Fuzzer-feedback coverage MUST reach the **target code**, not just the harness wrapper. Name the unit (library, package, module, class) that must carry it. Prebuilt artifacts from elsewhere — system libraries, distribution wheels, `.node` files, pre-built binary crates — do NOT carry instrumentation retroactively. The design must call for the target to be rebuilt under the fuzzer-feedback toolchain, or a pre-instrumented artifact to be located. If the project's build cannot accommodate that, say so — it is a signal to switch tools.
|
|
106
|
+
|
|
107
|
+
### Fallback path
|
|
108
|
+
|
|
109
|
+
Specify an alternative tool that uses a **different instrumentation mechanism** — not just a different frontend on the same one. A fallback on the same mechanism inherits the same failure mode. Canonical pairing: libFuzzer ↔ AFL++ (sanitizer-coverage vs. compile-wrapper). Cross-stack pairings (Jazzer ↔ Kelinci on JVM, atheris ↔ python-afl on Python) work the same way.
|
|
110
|
+
|
|
111
|
+
## Existing fuzzer selection
|
|
112
|
+
|
|
113
|
+
For large input spaces, structured inputs, or whenever coverage-guided exploration would outperform a hand-rolled loop, design the harness around an existing fuzzer rather than reinventing one. The build agent installs the tool; the design specifies WHICH tool, WHY, and the entry-point shape.
|
|
114
|
+
|
|
115
|
+
| Stack | Canonical fuzzer | Entry-point shape |
|
|
116
|
+
| --------------- | ---------------------- | ---------------------------------------------------------------------------- |
|
|
117
|
+
| C / C++ | libFuzzer (in-process) | `extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size)` |
|
|
118
|
+
| C / C++ | AFL++ (out-of-process) | Standard `main` reading from stdin or a file path; coverage via compile wrap |
|
|
119
|
+
| JVM | Jazzer | `static void fuzzerTestOneInput(byte[] data)` or `FuzzedDataProvider` form |
|
|
120
|
+
| Python | atheris | `atheris.Setup([...], TestOneInput)` with `def TestOneInput(data: bytes)` |
|
|
121
|
+
| Go | `go test -fuzz` | `func FuzzXxx(f *testing.F) { f.Fuzz(func(t *testing.T, ...) { ... }) }` |
|
|
122
|
+
| Rust | `cargo fuzz` (libFuzzer)| `fuzz_target!(|data: &[u8]| { ... })` |
|
|
123
|
+
|
|
124
|
+
For small discrete spaces, bounded enumeration (a typed `for` loop) is fine — but the design must state the choice and why.
|
|
125
|
+
|
|
126
|
+
## Seed corpus and dispatch surface
|
|
127
|
+
|
|
128
|
+
A coverage-guided fuzzer is only as good as its starting corpus. The corpus must reach the dispatch surface — otherwise feedback-driven mutation never explores the variants behind the dispatcher.
|
|
129
|
+
|
|
130
|
+
- **Programmatic seeds for structured dispatchers.** If the target has a structured dispatch table (tag table, opcode switch, protocol message-type enum, state-machine driver), the design must specify seeds **generated programmatically from that table** — at least one per dispatch case. This guarantees every variant has a representative the mutator can build from.
|
|
131
|
+
- **Hand-curated deep-path seeds are additive.** A few hand-crafted inputs that drive a specific protocol exchange or codec path complement programmatic seeds; they do not substitute for them.
|
|
132
|
+
- **Small discrete targets.** Mark seed corpus N/A with a one-line reason — e.g., the input is a single 32-bit integer and the harness exhausts it.
|
|
133
|
+
|
|
134
|
+
## Diagnostic checkpoints
|
|
135
|
+
|
|
136
|
+
Every design must specify three verification steps that the implementer can execute before declaring the harness usable:
|
|
137
|
+
|
|
138
|
+
1. **Baseline reachability.** With a benign input, confirm the target function is executed (audit-coverage hit at the named function, log line, or trace marker).
|
|
139
|
+
2. **Input integrity.** Verify inputs arrive at the target function unmodified — no truncation by an outer wrapper, no encoding mangling, no unintended preprocessing.
|
|
140
|
+
3. **Fuzzer-feedback reach.** Specify a threshold the target code's fuzzer-feedback metric must meet after a short fuzz burst (e.g., libFuzzer `cov:` > N covering the target unit, AFL++ `edges_found` > M attributable to the target). If not met, the feedback chain is broken — the fuzzer is mutating but no signal from the target reaches it.
|
|
141
|
+
|
|
142
|
+
## Mitigation testing discipline
|
|
143
|
+
|
|
144
|
+
The harness MUST test against UNMODIFIED source code. Stripping a guard, weakening a check, or replacing a sanitizer-armed allocator with a permissive one tells you nothing about the production binary's behavior. If the hypothesized condition doesn't trigger within the swept range, document which code intercepts it and whether that code covers ALL relevant paths.
|
|
145
|
+
|
|
146
|
+
A Tier-1 harness with hardening stripped tells you nothing about production outcomes. Triage anchors on what the harness demonstrated under production-equivalent conditions.
|
|
147
|
+
|
|
148
|
+
## Delegate library realism
|
|
149
|
+
|
|
150
|
+
Mitigation discipline applies at the link layer too. A harness that links against a project-internal stub of an upstream library produces evidence about the stub, not about production. Every stub the design introduces must be classified.
|
|
151
|
+
|
|
152
|
+
- **Class A — I/O / allocator stubs.** Replace operations whose behavior is irrelevant to the violation site: fixed-buffer reads/writes, controlled-return allocators, no-op logging, deterministic clocks. Acceptable.
|
|
153
|
+
- **Class B — Validator / parser / decoder stubs.** Replace upstream components that perform trust-boundary validation in production: format parsers, input validators, signature/checksum verifiers, schema validators, dimension/type checkers inside delegate libraries. **Forbidden.** A Class B stub strips production validation at the linker level — equivalent to redefining `VALIDATE_INPUT` to a no-op. The violation site may be reachable in the harness while in production the upstream library rejects the trigger before it lands.
|
|
154
|
+
- **Class C — Non-production build configuration.** A third-party library or package the harness depends on, but built or installed with optional features/components disabled that production deployments typically enable (compile-time build flags, language build tags, optional-dependency extras, conditional-compilation gates). Equivalent to a Class B stub of those features — **forbidden**. For each such dependency the design must record how it was built or installed and confirm parity with the production-default build/install for that dependency. Note: this is about each dependency's *own* configuration, not about which dependency a consumer selected to use (e.g., a meson/feature-flag option that picks one backend over another is a consumer-side selection, not a Class C concern; the picked backend's *own* build options are).
|
|
155
|
+
|
|
156
|
+
The design must enumerate every stub it introduces and classify each. Any Class B stub or Class C build-flag mismatch is a redesign trigger, not a tradeoff. If the violation site is unreachable through the real upstream library because the library's own validation pre-empts the trigger, the bug is a latent code smell — not an exploitable vulnerability — and the hypothesis should be re-evaluated rather than papered over with a permissive stub.
|
|
157
|
+
|
|
158
|
+
If integrating the real upstream library requires a higher tier than the design currently states, escalate the tier. Class B stubbing is not an acceptable shortcut to keep a Tier 1 design viable.
|
|
159
|
+
|
|
160
|
+
## Infrastructure assumptions
|
|
161
|
+
|
|
162
|
+
List every external dependency (sanitizer toolchain, fuzzer binary, coverage tool, decoder library, container image, kernel feature like KASAN, hypervisor mode) and what the design falls back to if any are unavailable. An assumption that goes unstated becomes a bug in the build phase.
|
|
163
|
+
|
|
164
|
+
## Common pitfalls
|
|
165
|
+
|
|
166
|
+
- **Tier-1 for a cross-component target.** Reproducing the value flow across a stub instead of the real callee. The stub agrees with the hypothesis by construction; the bug doesn't reproduce, or reproduces falsely.
|
|
167
|
+
- **Audit coverage without fuzzer feedback.** The harness reports lines hit but the fuzzer was running blind — no feedback signal reached the target. The "forgot-to-instrument-the-target" pitfall.
|
|
168
|
+
- **Feedback instrumentation on the wrapper, not the target.** The fuzzer is happily following coverage in the harness scaffold; the target's code is not contributing edges. Symptom: `cov:` plateaus quickly; the target is barely exercised.
|
|
169
|
+
- **"Test a few values" unit testing.** The design enumerates 5–10 fixed scenarios. The boundary is somewhere in the other 4 billion values the type can hold.
|
|
170
|
+
- **Fallback on the same instrumentation mechanism.** The design lists "libFuzzer or honggfuzz" — both sanitizer-coverage based. If sanitizer-coverage fails to attach to the target, both fail.
|
|
171
|
+
- **Prebuilt-artifact assumption.** "We'll fuzz against the system-installed library" — system libraries do not carry the fuzzer-feedback instrumentation. Either rebuild the dependency or pick a different surface.
|
|
172
|
+
- **Hand-rolled loop for a structured input space.** Reinventing libFuzzer with a `for` loop over `rand()`. Coverage-guided fuzzers exist because feedback-driven mutation finds boundary values that uniform random does not.
|
|
173
|
+
- **Seed corpus omits dispatch arms.** Programmatic per-arm seeds were skipped; the fuzzer never explores past the first arm of the dispatcher because no input takes any other arm.
|
|
174
|
+
- **Hand-picked positive scenarios masquerading as a sweep.** Five "boundary cases" are chosen by hand. The actual boundary in the target's arithmetic is not one of the five.
|
|
175
|
+
- **Hardening stripped for "easier debugging."** ASLR off, canaries off, custom permissive allocator. The harness fires; production does not. Tells you nothing.
|
|
176
|
+
- **Validator stubbed at link time.** A project-internal stub of a parser, validator, or decoder library is linked into the harness, omitting the upstream component's own validation. The harness fires; production rejects the trigger before the violation site is reached. Tells you nothing about reachability.
|
|
177
|
+
|
|
178
|
+
## Design-document scope
|
|
179
|
+
|
|
180
|
+
A design IS a specification. A design IS NOT an implementation.
|
|
181
|
+
|
|
182
|
+
Include:
|
|
183
|
+
|
|
184
|
+
- What source files to link and why
|
|
185
|
+
- What types, structs, and macros to preserve verbatim vs stub
|
|
186
|
+
- What input variables to sweep and over what range
|
|
187
|
+
- What conditions constitute a positive finding
|
|
188
|
+
- What instrumentation (sanitizer set, feedback mechanism, audit tool) to use
|
|
189
|
+
- The fallback tool and why its mechanism differs
|
|
190
|
+
- Diagnostic checkpoints with named thresholds
|
|
191
|
+
- Infrastructure assumptions and fallbacks
|
|
192
|
+
- Stub inventory and classification (Class A/B/C per *Delegate library realism*); designs declare every stub and, for each third-party library/package the harness depends on, the build-or-install-time configuration with production-default parity status
|
|
193
|
+
|
|
194
|
+
Exclude:
|
|
195
|
+
|
|
196
|
+
- Full C/Python/Java/etc. source code for the harness
|
|
197
|
+
- Build scripts or compile commands with full flag lists (reference them abstractly: "compile with ASAN+UBSAN+libFuzzer")
|
|
198
|
+
- Test execution results or tables of "expected outcomes" — nothing has been run yet
|
|
199
|
+
- Pre-enumerated `Test A / Test B / Test C` scenarios with fixed values
|
|
200
|
+
|
|
201
|
+
The design is read by an implementer who must turn it into running code. Be concrete enough that they don't have to guess at the spec; abstract enough that they aren't transcribing your code. If the design's body could compile, it has drifted into implementation.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-safety-c-cpp
|
|
3
|
+
description: Reference vocabulary for memory-safety vulnerabilities in native C/C++ code — bug-class taxonomy, common arithmetic patterns that lead to corruption, dispatch-family discipline, type-confusion idioms, use-after-free patterns, and exploitability factors. Read when analyzing, hypothesizing, designing harnesses for, or triaging findings against C/C++ code with sanitizer support (ASAN/UBSAN/TSAN/MSan). Not applicable to managed runtimes (JVM, .NET) or scripting languages — those have their own skills.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory-Safety Bugs in C/C++
|
|
7
|
+
|
|
8
|
+
Reference vocabulary for native C/C++ code with sanitizer support. Catalogs the bug classes, patterns, and exploitability factors a memory-safety investigation needs to reason about.
|
|
9
|
+
|
|
10
|
+
## Bug-class taxonomy
|
|
11
|
+
|
|
12
|
+
Identifiers below are the canonical IDs to use when naming a bug class in any artifact. Map each to the oracle that catches it and the root-cause CWE.
|
|
13
|
+
|
|
14
|
+
| Bug class | Oracle | Root-cause CWE | Where it lives |
|
|
15
|
+
| ---------------------- | -------------------------------------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| `heap_overflow` | ASAN `heap-buffer-overflow` | CWE-122 | Read or write past a heap allocation |
|
|
17
|
+
| `stack_overflow` | ASAN `stack-buffer-overflow` | CWE-121 | Read or write past a stack frame buffer |
|
|
18
|
+
| `out_of_bounds_read` | ASAN OOB-read | CWE-125 | Generic OOB load — distinct from heap/stack only by site |
|
|
19
|
+
| `out_of_bounds_write` | ASAN OOB-write | CWE-787 | Generic OOB store |
|
|
20
|
+
| `use_after_free` | ASAN `heap-use-after-free` | CWE-416 | Pointer dereferenced after the allocation it pointed to was freed |
|
|
21
|
+
| `double_free` | ASAN `attempting double-free` | CWE-415 | `free()` called twice on the same allocation |
|
|
22
|
+
| `integer_overflow` | UBSAN `signed-integer-overflow` / `unsigned-integer-overflow` | CWE-190 | Arithmetic produces a value outside the type's range |
|
|
23
|
+
| `type_confusion` | ASAN OOB or UBSAN `vptr` | CWE-843 | A value is interpreted as a different type than it was created with — frequently across vtable / dispatch-table boundaries |
|
|
24
|
+
| `format_string` | ASAN OOB / UBSAN | CWE-134 | Attacker-controlled bytes reach the format-spec argument of a `printf`-family call |
|
|
25
|
+
| `null_deref` | SIGSEGV (caught by ASAN as `SEGV on unknown address 0x000000000000`) | CWE-476 | Dereference of a pointer the attacker can force to NULL |
|
|
26
|
+
| `uninitialized_memory` | MSan `use-of-uninitialized-value` | CWE-457 | Read of memory before it was written |
|
|
27
|
+
| `race_condition` | TSAN `data race` | CWE-362 | Concurrent unsynchronized access (a TOCTOU is a special case where the racing accesses are check-then-use) |
|
|
28
|
+
| `memory_leak` | LSan / ASAN leak report | CWE-401 | Heap allocation lost without `free()` |
|
|
29
|
+
|
|
30
|
+
**CWE precision rule.** Always cite the _root-cause_ CWE, not the _consequence_ CWE. An integer overflow that leads to an undersized heap allocation and a subsequent OOB write is CWE-190 (the arithmetic bug), not CWE-122 (the consequence). A `strncpy` using the wrong size constant is CWE-806 (buffer access using size of source buffer), not CWE-120 (generic buffer overflow).
|
|
31
|
+
|
|
32
|
+
## Common arithmetic patterns that lead to corruption
|
|
33
|
+
|
|
34
|
+
These are the high-yield sites to look for whenever the surface processes attacker-controlled lengths, counts, offsets, or indices. Each is a _pattern_, not a procedure — the same pattern fires across many surfaces (parsers, codecs, compression, RPC framing, allocator wrappers).
|
|
35
|
+
|
|
36
|
+
- **Integer overflow into allocation size.** Attacker controls a count `n`. Code computes `n * sizeof(T)` (or `n + header`) and passes the result to `malloc`/`new[]`. The multiplication or addition wraps; the allocation is undersized; subsequent indexed writes overflow. The _named violation site_ is the multiplication or addition, not the OOB write — the OOB write is the consequence.
|
|
37
|
+
|
|
38
|
+
- **Signed/unsigned mixing at a comparison.** A signed integer is compared against an unsigned length (or vice versa). The signed value is implicitly converted to unsigned, turning negative values into very large positives. A "guard" of the form `if (i < len) ...` admits every negative `i` when `len` is unsigned.
|
|
39
|
+
|
|
40
|
+
- **Type narrowing at a storage boundary.** A wider type (computed precisely) is stored into a narrower container (`int` → `uint16_t`, `size_t` → `uint32_t`, `double` → `float`). The maximum value the source can reach exceeds the destination's range. Trace every write where source and destination types differ in width.
|
|
41
|
+
|
|
42
|
+
- **Unbounded growth meeting fixed-size storage.** A counter or accumulator grows without a hard cap, then is stored in or compared against a fixed-width field. When the counter reaches the maximum representable value of the storage type, the comparison flips or the stored value collides with a sentinel.
|
|
43
|
+
|
|
44
|
+
- **Sentinel and magic value reachability.** Identify every sentinel or magic value the code uses (`-1`, `0`, `NULL`, `0xFFFF`, `INT_MAX`, special tags). For each, ask whether any computed value — through normal execution, overflow, truncation, or wrapping — can equal the sentinel. Counters that grow toward a sentinel are a high-yield target.
|
|
45
|
+
|
|
46
|
+
- **Off-by-one at a buffer boundary.** Loop bound is `<=` instead of `<`, or vice versa. `strncpy` writes a non-null-terminated string. `memcpy` length is `len + 1` where `+1` was meant for a separate terminator.
|
|
47
|
+
|
|
48
|
+
- **Check-use separation (TOCTOU).** A value is validated in one place and consumed in another. Between check and use, the value can change — through concurrent access, callbacks, re-reads from shared / global state, or intervening function calls that mutate it.
|
|
49
|
+
|
|
50
|
+
- **Arithmetic result range vs destination range.** A multiplication, addition, or shift produces a value that exceeds the range of its destination type or comparison operand. Worth tracing whether intermediate computations widen the type before narrowing.
|
|
51
|
+
|
|
52
|
+
## Common non-arithmetic patterns
|
|
53
|
+
|
|
54
|
+
These produce memory corruption without an arithmetic root cause. They cluster around contract violations between functions — a writer leaves a buffer in a state the reader does not expect, or a sink assumes an invariant the source did not enforce. Apply alongside the arithmetic patterns; the bug at any given site may be in either category, and a surface that admits one usually admits both.
|
|
55
|
+
|
|
56
|
+
- **Missing null-termination on string sinks.** A function copies `n` bytes from a `(count, ptr)` pair into a buffer without appending a NUL. A downstream `printf("%s", ...)`, `strlen`, `strcat`, or `strchr` reads past the allocation searching for a terminator that does not exist. Particularly common in parsing routines that copy attacker-controlled strings field-by-field; the named violation site is the read sink, but the root cause is the writer's missing terminator.
|
|
57
|
+
|
|
58
|
+
- **Format-string controlled by attacker.** Attacker bytes reach the format-string argument of a `printf`-family call (`printf(user)` instead of `printf("%s", user)`). Yields memory disclosure via `%s` / `%n` and crashes via malformed specifiers. Trace every variadic-print call where the first argument is not a compile-time constant.
|
|
59
|
+
|
|
60
|
+
- **Length pair desynchronized from buffer.** A `(ptr, len)` interface is called with `len` exceeding the allocation behind `ptr`. Common when the length is read from input and the pointer is allocated to a different size, or when the buffer was reallocated but a stale length is used elsewhere. The bound check happens in one function; the read or write happens in another that assumes the check was honored.
|
|
61
|
+
|
|
62
|
+
- **Sentinel-driven iteration without an independent bound.** A loop walks until it hits a sentinel (`NUL`, `0xFFFF`, end-marker tag) without a hard cap. If the data lacks the sentinel — because validation did not enforce it, or the attacker stripped it — iteration runs past the buffer. Pairs naturally with terminated-string or terminated-list assumptions on input that has not been verified to terminate.
|
|
63
|
+
|
|
64
|
+
- **Print, dump, and diagnostic paths trust upstream invariants.** Print and dump routines often assume the data they receive has already been validated — terminated, bounded, well-formed — because they run after the parser. If validation skipped fields the print path consumes (common for "diagnostic" or "verbose" outputs), the print path becomes the OOB-read site even though the bug is the validator's omission. Every code path used only by `-v` / `-D` / `--debug` flags is its own attack surface that typically gets less validation attention than the main path.
|
|
65
|
+
|
|
66
|
+
## Dispatch-family discipline
|
|
67
|
+
|
|
68
|
+
When the target dispatches over a typed surface — a per-bps codec table, a per-message-type handler array, a per-opcode switch, a per-mode initializer, a per-format decoder — **every variant is its own attack surface**. Variants commonly share a contract on paper but differ in per-element arithmetic, buffer sizes, or guard placement.
|
|
69
|
+
|
|
70
|
+
- Treat each variant as a separate site for every pattern in this skill. "Same as variant X" is not a sufficient dismissal because variants commonly have differing per-element arithmetic.
|
|
71
|
+
- A coverage-gap finding is structural: if existing fuzzers exercise three of the seven variants, the other four are under-tested by the project itself.
|
|
72
|
+
- Variant selection often happens early (a header byte, a tag enum). The dispatch site itself is rarely vulnerable; the per-variant body usually is.
|
|
73
|
+
|
|
74
|
+
## Type-confusion in C++ vtables and dispatch tables
|
|
75
|
+
|
|
76
|
+
C++ adds a class of bugs that don't appear in pure C surfaces:
|
|
77
|
+
|
|
78
|
+
- **Bad downcast.** A `static_cast<Derived*>(base_ptr)` where `base_ptr` does not point to a `Derived`. Subsequent virtual calls invoke the wrong method; subsequent member accesses read or write at the wrong offsets.
|
|
79
|
+
- **Lifetime-vs-vtable race.** A method on `this` runs after the object's destructor has already started — common in callback handlers, signal handlers, and worker-thread teardown. `vptr` may have been overwritten or freed.
|
|
80
|
+
- **Polymorphic container element-type drift.** A `std::variant`, tagged union, or `void*`-keyed map records one type but is read as another, often after a refactor that added a new variant tag without updating every consumer.
|
|
81
|
+
|
|
82
|
+
UBSAN's `vptr` check (`-fsanitize=vptr`) catches some of these; the rest surface as `heap-use-after-free` or as silent corruption.
|
|
83
|
+
|
|
84
|
+
## Use-after-free patterns
|
|
85
|
+
|
|
86
|
+
UAFs cluster around lifetime confusion, not single-line bugs. The high-yield search patterns:
|
|
87
|
+
|
|
88
|
+
- **Lifetime bugs in callbacks.** A callback retains a pointer or reference to an object whose owner unregisters and frees it. The next callback invocation dereferences a freed pointer.
|
|
89
|
+
- **Freed-on-error paths.** An error handler frees a resource that the success path also frees. Either path alone is fine; the bug is in the joint state space — frequently exposed by partial failures inside a loop.
|
|
90
|
+
- **Double-free via aliasing.** Two pointers refer to the same allocation; both are passed through cleanup paths. A double-free is the symptom; the root cause is unclear ownership.
|
|
91
|
+
- **Stale iterator / span / reference.** A container is mutated (resize, erase, rehash) while an iterator, `string_view`, `span`, or raw pointer derived from it is held. The reference dereferences memory the container has moved or freed.
|
|
92
|
+
- **Object-resurrection patterns.** A reference-counted object's count drops to zero and is re-incremented before destruction completes — typically through a weak-pointer race.
|
|
93
|
+
|
|
94
|
+
## Primitive-extent scaling axes
|
|
95
|
+
|
|
96
|
+
Each memory-safety primitive class has natural scaling axes — the dimensions along which a minimum trigger can be pushed to characterize what an attacker can actually do with the primitive. Discover exercises these axes to produce adversary-maximal evidence; triage scores on the demonstrated extent. Mirrored from `vulnerability-triage`'s *Primitive extent* section so this skill (loaded by `discover` and `analyze`) carries the same canonical enumeration triage uses.
|
|
97
|
+
|
|
98
|
+
- **Out-of-bounds write/read.** Push distance past the allocation, total bytes accessed, attacker control over the written/read byte values, and stride between accesses. The minimum trigger may demonstrate one byte past one allocation with a fixed value; the axes ask whether that extends to attacker-chosen bytes across a larger span at a controllable stride.
|
|
99
|
+
- **Use-after-free.** Push reuse delay against any deferred-free queue or generation counter, and characterize the type set that can land in the freed slot under attacker-influenceable allocation pressure.
|
|
100
|
+
- **Type confusion.** Push the type-pair set the confusion lands on across the surrounding dispatch, and the depth of polymorphic dispatch reached in the confused state before the program faults or returns.
|
|
101
|
+
- **Integer overflow into undersized allocation.** Push the undersize ratio between requested and allocated size, and the extent of the downstream write or read that consumes the un-truncated value.
|
|
102
|
+
- **Unbounded iteration.** Push the iteration count against the buffer or frame the iteration writes into, and the attacker control over the value written at each step.
|
|
103
|
+
- **Resource exhaustion / DoS.** For primitives whose impact is allocation/CPU/IO exhaustion rather than memory corruption, the axes shift. Push file-size-to-effect amplification (peak RSS, CPU time, or FD count divided by input file size on disk); wire-body-to-effect amplification when transport compression is in scope (same numerator divided by compressed POST body size — material for AV:N primitives, where gzip/br/zstd handed by upstream proxies can dominate cost-to-attacker); per-item-size × item-count multiplication when the bug multiplies via list/array containers (both ceilings are attacker-controlled — document independently); and the allocation-timing locus (eager parse before any application code runs vs. lazy on-demand — determines where in the call graph mitigations can be sited).
|
|
104
|
+
|
|
105
|
+
If this list and `vulnerability-triage`'s *Primitive extent* section drift, fix both at once — triage's scoring rule and discover's evidence-gathering rule must reference the same axes.
|
|
106
|
+
|
|
107
|
+
## Exploitability reference
|
|
108
|
+
|
|
109
|
+
Whether a confirmed sanitizer crash is a real security risk depends on the runtime environment. This is reference material for triage, not a procedure.
|
|
110
|
+
|
|
111
|
+
**Mitigation timeline (shifts what's reachable):**
|
|
112
|
+
|
|
113
|
+
| Mitigation | Effect |
|
|
114
|
+
| -------------------------- | --------------------------------------------------------------- |
|
|
115
|
+
| Full RELRO | No GOT, no `.fini_array`, no `.init_array` writeable at runtime |
|
|
116
|
+
| glibc 2.34+ | `__malloc_hook` / `__free_hook` removed |
|
|
117
|
+
| glibc 2.38+ | `%n` may be blocked in some configurations — check empirically |
|
|
118
|
+
| Stack canary + no infoleak | Cannot overflow past the canary without first leaking it |
|
|
119
|
+
| ASLR + no infoleak | Cannot find absolute addresses to write |
|
|
120
|
+
|
|
121
|
+
**Constraint rules to apply:**
|
|
122
|
+
|
|
123
|
+
- **strcpy on x86_64 writes 6 bytes per address.** A NULL-terminated `strcpy` copy writes the 6 low bytes of an address before the NULL byte stops it; the upper 2 bytes are not written. This bounds what a `strcpy`-based primitive can land.
|
|
124
|
+
- **Heap exploitation depends on the glibc version actually shipped.** `tcache` poisoning, `unsorted_bin` attacks, and house-of-\* techniques all have version windows. Cite the _deployed_ glibc, not the latest.
|
|
125
|
+
- **One-gadget feasibility is constraint-bounded.** A one-gadget needs registers and memory to be in specific states at the moment of call. A gadget that exists in `libc` is not the same as a gadget that's reachable from the crash state.
|
|
126
|
+
|
|
127
|
+
**When a sanitizer crash is NOT a security bug:**
|
|
128
|
+
|
|
129
|
+
- Assertion in an unreachable path (the harness reaches it because invariants the production code enforces were skipped in stubs).
|
|
130
|
+
- NULL-deref on `malloc` failure inside a path that the production environment never reaches under attacker pressure.
|
|
131
|
+
- Stack overflow at a recursive parser given input that would be rejected by an upstream length check the harness skipped.
|
|
132
|
+
- Uninitialized memory read on a path the production code never executes (debug-only branches, dead code).
|
|
133
|
+
|
|
134
|
+
The triage discipline is to anchor severity on what the harness _actually demonstrated under production-equivalent conditions_, not on the theoretical maximum implied by the crash class. A Tier-1 harness with hardening stripped tells you nothing about production outcomes.
|
|
135
|
+
|
|
136
|
+
## Hypothesis phrasing for memory-safety bugs
|
|
137
|
+
|
|
138
|
+
Hypotheses must be **value-level**, not pattern-level.
|
|
139
|
+
|
|
140
|
+
- Bad: "Supplying 24+ bytes overwrites the return address."
|
|
141
|
+
- Good: "At input offset 24, RIP=`0x4141414141414141` → SIGSEGV at attacker-controlled address."
|
|
142
|
+
|
|
143
|
+
- Bad: "There may be integer overflow in the parser."
|
|
144
|
+
- Good: "When the input field at `<file>:<line>` exceeds `2^31 - 1`, the multiplication at `<file>:<line>` wraps to a negative `int`, and the subsequent `malloc(n * sizeof(T))` allocates fewer bytes than the loop at `<file>:<line>` then writes."
|
|
145
|
+
|
|
146
|
+
A well-formed memory-safety hypothesis names: (1) the input value or shape that triggers the pattern, (2) the named arithmetic / dereference / lifetime site that violates, (3) the oracle that should fire (specific ASAN error class, UBSAN check, TSAN race, or assertion), and (4) the root-cause CWE.
|
|
147
|
+
|
|
148
|
+
## Observable-effect requirement
|
|
149
|
+
|
|
150
|
+
A finding is supported only by **observable evidence**: a crash, sanitizer trace, changed output, callback fired, file read, error message, or measurable state change. "Ran without error" is not evidence. If the expected effect is not observed, either the harness is wrong or the bug is not triggered — diagnose which; do not paper over.
|
|
151
|
+
|
|
152
|
+
When claiming a finding is exploitable, the supporting evidence must answer all three:
|
|
153
|
+
|
|
154
|
+
1. What specific input shape triggers it?
|
|
155
|
+
2. What dangerous operation does it reach (and how, exactly)?
|
|
156
|
+
3. What capability does an attacker gain that they did not have before?
|
|
157
|
+
|
|
158
|
+
If any of the three cannot be answered from the artifacts alone, the finding is not yet exploitable — it is a detector hit awaiting demonstration.
|
|
159
|
+
|
|
160
|
+
## Common pitfalls when reasoning about C/C++ memory safety
|
|
161
|
+
|
|
162
|
+
- **Confusing detector evidence with impact evidence.** A sanitizer fired on a Tier-1 isolated function tells you the detector caught something; it does not tell you the attacker can demonstrate impact in production. Padding, runtime guards, schema validators, sanitizing middleware, or attacker-uncontrolled state often absorb the effect.
|
|
163
|
+
- **Round-tripped attacker input mistaken for leaked target state.** If the bytes the bug "leaks" are bytes the attacker just sent, that is not a confidentiality finding. Memory-read findings need to demonstrate adjacency — what _target_ state sits next to the affected region.
|
|
164
|
+
- **Padding or zeroed memory mistaken for sensitive content.** A heap OOB read that returns NUL bytes, schema-sanitized placeholders, or default-config values has not demonstrated impact.
|
|
165
|
+
- **Hardening-stripped harness vs production binary.** A harness compiled without `-D_FORTIFY_SOURCE`, without stack canaries, without ASLR, or with custom allocators tells you nothing about the production binary's behavior. The triage question is "what does the production build do under this input," not "what does my Tier-1 reproduction show."
|
|
166
|
+
- **Variant tunnel-vision.** Reporting only the variant the harness reproduced when the underlying pattern (e.g., the same arithmetic in a sibling dispatch handler) reaches more of the surface. Every variant is its own attack surface; document each.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vulnerability-triage
|
|
3
|
+
description: Reference vocabulary for interpreting vulnerability findings — detector-vs-impact distinction, severity anchoring on demonstrated evidence, the eleven-item interpretation rubric, delegation transparency, primitive-extent scaling, the disqualifier taxonomy (D-0..D-4), CVSS Achievable / Environmental framing, hedging-phrase elimination, and falsification asymmetry. Read when interpreting a finding to decide whether the demonstrated evidence supports the severity it would warrant. Surface-neutral; pulls in the relevant surface skill (e.g. `memory-safety-c-cpp`) for bug-class-specific exploitability factors.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vulnerability Triage
|
|
7
|
+
|
|
8
|
+
Reference vocabulary for interpreting what an investigation actually demonstrated and translating it into threat-model language. Catalogs the rubric, disqualifiers, scoring conventions, and pitfalls a triage decision needs to reason about.
|
|
9
|
+
|
|
10
|
+
## Core rule
|
|
11
|
+
|
|
12
|
+
Severity is anchored on what was actually demonstrated, never on the theoretical maximum implied by the vulnerable code. A detector firing on an isolated harness — sanitizer, fuzz crash, static analyzer hit, tainted-flow report, schema-validator alert, assertion failure — proves the detector caught something. It does NOT prove attacker-visible impact. Confidentiality, integrity, and availability claims require the corresponding effect to have been observed in production-equivalent conditions. When the demonstrated observation is weaker than the theoretical ceiling, score the demonstrated level and label the ceiling as such.
|
|
13
|
+
|
|
14
|
+
## Detector evidence vs impact evidence
|
|
15
|
+
|
|
16
|
+
These two are routinely confused. Hold the line between them.
|
|
17
|
+
|
|
18
|
+
**Detector evidence** is anything a tool flagged: ASAN / UBSAN / TSAN / MSan crash, libFuzzer reproducer, static-analyzer warning, taint-flow trace, assertion site, schema-validator alert, AST pattern match. Detector evidence proves the detector caught an anomaly at the named site. It says nothing on its own about whether an attacker can reach that site, supply attacker-useful values, or observe the result.
|
|
19
|
+
|
|
20
|
+
**Impact evidence** is an observation of an attacker-visible CIA effect under production-equivalent conditions: reading bytes the attacker did not previously hold, mutating state the attacker did not previously control, denying service to a user other than the attacker, gaining a capability the attacker did not previously have. Impact evidence is anchored on observable artifacts — a crash, sanitizer trace, changed output, callback fired, file read, error message, measurable state change.
|
|
21
|
+
|
|
22
|
+
"Ran without error" is not impact evidence. If the expected effect was not observed, either the experiment was wrong or the bug is not triggered — diagnose which; do not paper over. A finding supported only by detector evidence is a detector hit awaiting demonstration, not a confirmed vulnerability.
|
|
23
|
+
|
|
24
|
+
## Interpretation rubric
|
|
25
|
+
|
|
26
|
+
For each finding, the rubric below has eleven items. Skipping any one of them is a triage failure. Each item names a question, what counts as a complete answer, and what an incomplete answer looks like.
|
|
27
|
+
|
|
28
|
+
1. **Reproduction.** Re-run the exact input. Confirm the same observation. Complete answer: same input, same observation, same artifact (crash signature, sanitizer trace, observed output). Incomplete: "should reproduce", "did not retry", or a different observation than the original. If reproduction fails, the finding is not yet triageable.
|
|
29
|
+
|
|
30
|
+
2. **Effect realism.** Classify the observation against the four categories below; the first two support an impact claim, the last two refute it.
|
|
31
|
+
- Attacker-uncontrolled target state observed (genuine impact).
|
|
32
|
+
- Attacker-controlled effect on state the attacker did not previously hold (genuine impact).
|
|
33
|
+
- Attacker's own input echoed or round-tripped back, or constant / neutral state (zeroed memory, schema-sanitized placeholder, default config, empty collection) — no impact.
|
|
34
|
+
- Non-exploitable error path the attacker cannot steer — no impact.
|
|
35
|
+
|
|
36
|
+
Reject impact claims when the observation falls in the last two categories.
|
|
37
|
+
|
|
38
|
+
3. **Attacker control surface.** Is the effect's extent adversary-chosen, or bounded by structure the attacker cannot influence? Complete answer: explicitly state which input fields drive the effect's size, location, or content, and which are fixed by the surrounding code. Incomplete: "the attacker controls the input" without naming which subset of the input controls which dimension of the effect.
|
|
39
|
+
|
|
40
|
+
4. **Adjacency / payload model.** What attacker-useful content plausibly reaches the affected location in production state? For memory-read findings, what target state sits adjacent to the affected region. For injection findings, what sink does the tainted value reach. For auth-bypass findings, what resources become reachable. State this as a reasoned model unless the experiment measured the specific payload.
|
|
41
|
+
|
|
42
|
+
5. **Channel reality.** In deployed configurations, does the attacker actually observe the effect? Some channels realize impact (metadata APIs, attacker-rendered output, error messages echoed back). Others absorb or drop it (batch jobs, log-only sinks, sandboxed processes, observation channels not exposed to the caller). Complete answer: name the production channel through which the effect would be observed; trace it from the bug site to where the attacker sees it.
|
|
43
|
+
|
|
44
|
+
6. **Production hardening.** What mitigations present in production (sanitizers, schema validators, CSP, ASLR, stack canaries, auth middleware, sandbox, rate limits, RELRO, FORTIFY_SOURCE) change the attacker's success rate? Did the experiment use the production profile or bypass it? A harness with hardening stripped tells you nothing about production outcomes — score what production does, not what the harness did.
|
|
45
|
+
|
|
46
|
+
7. **External exploitability trace.** Does the input originate from an external / untrusted source, and through what delivery channel? Name it concretely — network protocol, file upload, queue consumer, environment-variable propagation, IPC. Complete answer: a named delivery channel from an untrusted boundary to the bug site. Incomplete: "an attacker could supply input" without naming how.
|
|
47
|
+
|
|
48
|
+
8. **Duplicate / CVE check.** Search the project's issue tracker, CVE databases, and changelog for the code location. Note overlap with existing fixes even when partial — a fix for one variant does not necessarily cover sibling variants.
|
|
49
|
+
|
|
50
|
+
9. **Severity score.** CVSS 3.1 anchored on demonstrated evidence under the Achievable / Environmental framing below. Justify each metric against observations. If a metric is ambiguous, pick the lower value and note what work would support the higher.
|
|
51
|
+
|
|
52
|
+
10. **Delegation transparency.** Skill section: *Delegation transparency*. If the finding depends on a delegate library, both linkage realism and delegate-side validation analysis are present. If either is missing, the score caps at Low regardless of other rubric items.
|
|
53
|
+
|
|
54
|
+
11. **Primitive extent.** Skill section: *Primitive extent*. The scaling axes for this finding's primitive class are enumerated. For each, discover either demonstrated the axis or it is documented as un-explored. Un-explored axes that would change severity drive an `insufficient` verdict with directive — they do NOT raise the achievable score on extrapolation alone. For memory-safety findings, impact surface and heap layout reachability are explicitly analyzed.
|
|
55
|
+
|
|
56
|
+
## Delegation transparency
|
|
57
|
+
|
|
58
|
+
When the violation site depends on values produced by a delegate library — any upstream parser, validator, decoder, or runtime not under the project's source control — severity cannot anchor beyond "latent code smell" until the delegate's own validation has been confirmed to allow the trigger values through. A harness that links against a project-internal stub of the delegate falsely demonstrates reachability: the trigger reaches the violation site because the stub's permissive behavior bypassed the real delegate's validation, not because the real delegate would.
|
|
59
|
+
|
|
60
|
+
Before scoring any severity above Low (CVSS ~3.x) on a finding that depends on a delegate library, two confirmations are required:
|
|
61
|
+
|
|
62
|
+
1. **Linkage realism.** Cite evidence that the harness links against the real delegate library version production would link — not a project-internal stub. Acceptable evidence: dynamic linker output (`ldd`, `otool -L`), build manifest, package version pin. Project-internal stub directories (typically named `*-stub/`, `*_mock/`, `mock-*/`, `fake-*/`) resolved by the linker disqualify the evidence.
|
|
63
|
+
2. **Delegate-side validation analysis.** Cite the specific delegate-side check that the trigger must pass through (function name, file, validation expression) and explain why it allows the trigger values through. If no such check exists in the delegate, state that explicitly with citation to the delegate's source.
|
|
64
|
+
|
|
65
|
+
Without both, the finding is a hardening opportunity at most. Score Low, recommend the upstream-side guard as defense-in-depth, and flag for harness redesign.
|
|
66
|
+
|
|
67
|
+
This rule sits above the rubric — failing it caps the achievable score regardless of the rubric's other items.
|
|
68
|
+
|
|
69
|
+
## Primitive extent
|
|
70
|
+
|
|
71
|
+
The achievable CVSS score anchors on what discover actually demonstrated, including the scaling extent discover exercised — not on a larger scale the source suggests is reachable but no experiment ran. A primitive's theoretical ceiling implied by the surrounding code is not impact evidence; that is the same projection the Core rule forbids.
|
|
72
|
+
|
|
73
|
+
Triage's primitive-extent job is **enumeration and gap-checking**, not extrapolation. For each finding, list the scaling axes that apply to the primitive class. For each axis, classify whether discover demonstrated it (and at what value) or left it un-explored. An un-explored axis whose value would change severity if exercised is a triage gap, not a license to score on the projected value.
|
|
74
|
+
|
|
75
|
+
When an un-explored axis would change severity, the verdict is `insufficient` with a directive that names the axis, the boundary value to push toward, and what discover should run to characterize it. This is one of the concrete triggers for the insufficient-verdict discipline elsewhere in this skill — the achievable score does NOT rise on extrapolation alone. If every axis that matters has been demonstrated by discover (even at a non-maximum value), score on the demonstrated extent and note which axes were sampled where.
|
|
76
|
+
|
|
77
|
+
Common scaling axes per primitive class — for each, the entry names what discover should push to characterize the axis:
|
|
78
|
+
|
|
79
|
+
- **Out-of-bounds write/read.** Discover should push distance past the allocation, total bytes accessed, attacker control over the written/read byte values, and stride between accesses. The minimum trigger may demonstrate one byte past one allocation with a fixed value; the axes ask whether that extends to attacker-chosen bytes across a larger span at a controllable stride.
|
|
80
|
+
- **Use-after-free.** Discover should push reuse delay against any deferred-free queue or generation counter, and characterize the type set that can land in the freed slot under attacker-influenceable allocation pressure.
|
|
81
|
+
- **Type confusion.** Discover should push the type-pair set the confusion lands on across the surrounding dispatch, and the depth of polymorphic dispatch reached in the confused state before the program faults or returns.
|
|
82
|
+
- **Integer overflow into undersized allocation.** Discover should push the undersize ratio between requested and allocated size, and the extent of the downstream write or read that consumes the un-truncated value.
|
|
83
|
+
- **Unbounded iteration.** Discover should push the iteration count against the buffer or frame the iteration writes into, and the attacker control over the value written at each step.
|
|
84
|
+
- **Resource exhaustion / DoS.** For primitives whose impact is allocation/CPU/IO exhaustion rather than memory corruption, discover should push file-size-to-effect amplification (peak RSS, CPU time, or FD count divided by input file size on disk); wire-body-to-effect amplification when transport compression is in scope (same numerator divided by compressed POST body size — material for AV:N primitives, where gzip/br/zstd handed by upstream proxies can dominate cost-to-attacker); per-item-size × item-count multiplication when the bug multiplies via list/array containers (both ceilings are attacker-controlled — document independently); and the allocation-timing locus (eager parse before any application code runs vs. lazy on-demand — determines where in the call graph mitigations can be sited).
|
|
85
|
+
|
|
86
|
+
Score the achievable severity on the maximum extent discover demonstrated across these axes. If discover only ran the minimum trigger and an axis above plausibly changes severity, return `insufficient` with the axis named in the directive — do not silently raise the score, and do not silently cap it without flagging the gap.
|
|
87
|
+
|
|
88
|
+
When evaluating a heap OOB primitive, two additional analyses feed the impact axis. These reason over facts already in evidence (struct layouts, allocation order in the source, the crafted trigger's input shape) rather than projecting from one demonstrated trigger to a different one — they are static-analysis triage, not extrapolation:
|
|
89
|
+
|
|
90
|
+
- **Impact surface.** Enumerate the adjacent allocation classes within the write stride: object vtables, library-internal struct fields, allocator metadata, and similar. Name what corruption produces, and which CVSS axis each corruption shape feeds (I, A, C). The enumeration must be concrete (named struct types, named fields) — "memory corruption could affect anything" is not enumeration.
|
|
91
|
+
- **Heap layout reachability.** Is the heap state attacker-influenceable from the same input that triggers the bug? If yes, attack complexity drops a step (heap grooming is in-band rather than requiring a separate vector). If no, name the limiting factor (out-of-arena allocations, fresh process per request, deterministic allocator).
|
|
92
|
+
|
|
93
|
+
These two analyses are answerable from artifacts the workflow already produces (struct layouts, allocation traces, the crafted trigger input). They drive the CVSS Integrity and Attack Complexity scores; they are not optional for memory-safety findings.
|
|
94
|
+
|
|
95
|
+
**Mitigation reachability.** The abort site (function, file, line) where discover's demonstrated trigger actually faults or aborts is part of the primitive's characterization — record it alongside the scaling axes. Any mitigation triage recommends or accepts must sit at or upstream of that abort site in the call graph. A mitigation in code reachable only *after* the abort fires is unreachable in the demonstrated attack path and is not a valid recommendation, even if the static analysis of the surrounding code suggests it would catch the trigger. This check is most often violated when the abort fires inside a delegate library during eager parse and the proposed fix sits in application-side code that runs after parse completes.
|
|
96
|
+
|
|
97
|
+
## Disqualifier taxonomy
|
|
98
|
+
|
|
99
|
+
When the verdict is `insufficient`, the supporting notes carry one of the labels below. The labels disambiguate the gap and let downstream routing target it. The taxonomy is for justifying gaps in already-presented findings — not for seek-and-find dismissal of findings that meet the rubric.
|
|
100
|
+
|
|
101
|
+
- **D-0 — Evidence-synthesis failure.** A prior experiment or analysis disproved the hypothesis the finding now rests on, but the finding was assembled without integrating that result. The contradiction is in the artifacts; the finding ignored it.
|
|
102
|
+
|
|
103
|
+
- **D-1 — Test, mock, example, or documentation code.** The bug site lives in non-production code: unit test fixtures, example harnesses, sample inputs in documentation, mock implementations, debug-only utilities. Production callers do not reach it.
|
|
104
|
+
|
|
105
|
+
- **D-1.5 — Privilege tautology.** The "attack" requires the attacker to already hold the capability the bug supposedly grants. A vulnerability must harm someone other than the attacker. Root reading a root-owned file, an admin reading admin-only data through an admin-only path, or a process reading its own memory through its own debug interface is not a vulnerability.
|
|
106
|
+
|
|
107
|
+
- **D-2 — Disqualifying preconditions.** The bug requires conditions the attacker cannot supply: chaining with a separate unconfirmed vulnerability, a victim taking a specific action, physical access to the host, prior authentication where the bypass is not itself the finding, a non-default configuration the deployer would not reasonably enable.
|
|
108
|
+
|
|
109
|
+
- **D-3 — Hedged claim that did not survive verification.** The finding's severity claim depends on language that hedges over the evidence rather than stating it (see "Hedging-language elimination" below). When the hedge is stripped, the claim collapses or contradicts the artifacts.
|
|
110
|
+
|
|
111
|
+
- **D-4 — No security impact.** The bug class is real, but no CIA effect follows. Memory leak without amplification, resource exhaustion without disproportion, cosmetic state corruption that no consumer reads, an observation channel that absorbs the effect before any caller sees it, debug-only side effects.
|
|
112
|
+
|
|
113
|
+
## CVSS Achievable / Environmental scoring
|
|
114
|
+
|
|
115
|
+
Triage emits one CVSS 3.1 vector and score per finding. That score is the **Achievable / Environmental** number — it reflects what was demonstrated under production-equivalent hardening, not the inherent worst case the bug class could imply on a hardening-stripped target.
|
|
116
|
+
|
|
117
|
+
Conventions for the metrics:
|
|
118
|
+
|
|
119
|
+
- **AV (Attack Vector).** Decomposes how the attacker reaches the *vulnerable code*, not how they reach the host. A network-reachable service whose vulnerable function requires a local side-channel to trigger is AV:L for that finding, even though the service is on the network.
|
|
120
|
+
|
|
121
|
+
- **PR (Privileges Required).** Decomposes the privileges needed to reach the *vulnerable path*, not the privileges needed to log into the system at all. If the vulnerable path is gated behind admin-only middleware, PR:H — even if the broader service has anonymous endpoints elsewhere.
|
|
122
|
+
|
|
123
|
+
- **UI / S / C / I / A.** Anchored on what was observed under the production hardening profile. If production hardening absorbs the effect (a schema validator strips the payload; padding zeros the leak; a sandbox contains the side effect), the impact metric drops to None for the absorbed dimension and the absorption is noted.
|
|
124
|
+
|
|
125
|
+
- **Hardening-stripped harness contributes nothing here.** A T1 harness compiled without canaries, ASLR, FORTIFY_SOURCE, or with custom permissive allocators may score very high in the abstract; that score is not the Achievable / Environmental score. If the production profile was not exercised, score what the production profile would do and call out the gap.
|
|
126
|
+
|
|
127
|
+
## Hedging-language elimination
|
|
128
|
+
|
|
129
|
+
Hedged claims drift severity upward without evidence. Every hedge below must either be verified to a value-level statement or struck from the finding entirely. They are claims-to-verify, not claims-to-include.
|
|
130
|
+
|
|
131
|
+
- "could potentially"
|
|
132
|
+
- "may be exploitable"
|
|
133
|
+
- "in theory"
|
|
134
|
+
- "subject to specific conditions"
|
|
135
|
+
- "if configured incorrectly"
|
|
136
|
+
- "low likelihood"
|
|
137
|
+
- "informational risk"
|
|
138
|
+
- "cannot be ruled out"
|
|
139
|
+
- "depending on environment"
|
|
140
|
+
- "under certain circumstances"
|
|
141
|
+
- "in some configurations"
|
|
142
|
+
|
|
143
|
+
A finding that depends on hedging language to assert its severity is a D-3 disqualifier candidate. Verified replacements name the specific value, configuration, or condition that turns the hedge into an observation.
|
|
144
|
+
|
|
145
|
+
## Falsification asymmetry
|
|
146
|
+
|
|
147
|
+
One observed exploitation refutes "no bug." One failed exploitation does NOT refute "this bug." The burden is asymmetric: a single positive observation outweighs many failed attempts against the same evidence. Practical consequences:
|
|
148
|
+
|
|
149
|
+
- If any approach succeeds in demonstrating the attacker-visible effect, the finding has impact evidence — even if other approaches against the same hypothesis failed.
|
|
150
|
+
- If all plausible approaches failed against the same evidence, the finding is `insufficient` — not "ruled out." A different approach, a different harness tier, or a different production profile may still demonstrate impact.
|
|
151
|
+
- Negative results constrain the demonstrated severity; they do not prove the bug-class is absent. State what was tested and what was not, and what range of severities the negatives rule out.
|
|
152
|
+
|
|
153
|
+
## Common pitfalls
|
|
154
|
+
|
|
155
|
+
Patterns the rubric catches that get missed in practice. These are surface-neutral; per-surface restatements (e.g. heap-OOB-read adjacency rules, sanitizer-on-padding behavior) live in the relevant surface skill.
|
|
156
|
+
|
|
157
|
+
- **Detector evidence rebadged as impact evidence.** A sanitizer fired on a Tier-1 isolated function. The finding writeup translates that into "remote code execution" without ever naming where the production code observes the effect. The detector says the detector caught something; that is not the same sentence.
|
|
158
|
+
|
|
159
|
+
- **Round-tripped attacker input mistaken for leaked target state.** The bytes the bug "leaks" are the bytes the attacker just supplied, with possibly some encoding transformation. That is not a confidentiality finding. Any leak claim must demonstrate adjacency: what *target* state sits next to the affected region, and that the observation contained that target state.
|
|
160
|
+
|
|
161
|
+
- **Padding, zeroed memory, or sanitized placeholders mistaken for sensitive content.** An OOB read returns NUL bytes, schema-sanitized fields, or default-config values. The detector fired; impact has not been demonstrated.
|
|
162
|
+
|
|
163
|
+
- **Hardening-stripped harness scored as production.** The harness ran without ASLR, without canaries, without FORTIFY_SOURCE, with a permissive custom allocator, or with the validating middleware bypassed. The production binary intercepts the effect; the harness did not see that interception. The Achievable / Environmental score is what production does, not what the harness did.
|
|
164
|
+
|
|
165
|
+
- **Theoretical ceiling promoted to demonstrated severity by rewording.** "This pattern *could* yield RCE" gets quoted, then rewritten without the hedge, then cited as "demonstrated RCE" two paragraphs later. The rubric's reproduction and effect-realism items refute this; the hedging-elimination rule names it.
|
|
166
|
+
|
|
167
|
+
- **Variant tunnel-vision.** The harness reproduced one variant of the underlying pattern; the finding reports only that variant. When the same arithmetic / lifetime / dispatch shape reaches sibling variants on the same surface, those are findings in their own right. Document each, even when the experiment exercised only one.
|
|
168
|
+
|
|
169
|
+
- **Channel that absorbs the effect.** The bug site exists, the value is attacker-controlled, but the production channel between the bug site and any observer is a logging path, a batch job that runs offline, a sandbox whose violations never escape. The effect happened; the attacker cannot observe it. Score impact at None for the dimension the channel absorbs.
|
|
170
|
+
|
|
171
|
+
- **Privilege tautology dressed as a finding.** The "attacker" is the same principal as the "victim." A user reading their own data through their own session, an admin invoking an admin-only API, a process inspecting its own memory. D-1.5; not a vulnerability.
|
|
172
|
+
|
|
173
|
+
## Insufficient-verdict discipline
|
|
174
|
+
|
|
175
|
+
When evidence does not support the severity the finding would warrant, the verdict is `insufficient`. The notes accompanying that verdict MUST name the specific gap concretely:
|
|
176
|
+
|
|
177
|
+
- The parameter to sweep, with its named range and the boundary values not yet exercised.
|
|
178
|
+
- The mitigation to exercise, with the production profile to rebuild under and the hardening flags to enable.
|
|
179
|
+
- The adjacency to verify, with the target-state class that must be observed leaked rather than padding or attacker-input.
|
|
180
|
+
- The hardening profile to rebuild under, with the specific compile flags or runtime configuration the prior experiment skipped.
|
|
181
|
+
- The disqualifier label (D-0..D-4) that the finding currently triggers, when applicable.
|
|
182
|
+
|
|
183
|
+
Generic gap statements ("do more discovery", "needs more evidence", "investigate further") are forbidden. The downstream consumer of an `insufficient` verdict needs a directive concrete enough to act on without rederiving the gap.
|
|
184
|
+
|
|
185
|
+
A finding's `insufficient` verdict is not a closure — it is a redirection. The finding remains open; the gap statement is what closes it on the next round.
|