diff --git a/.clinerules b/.clinerules index 7fbb9e1..2141dea 100644 --- a/.clinerules +++ b/.clinerules @@ -1,7 +1,7 @@ # GuardLink Security Annotations -This project uses GuardLink annotations in source code comments or standalone .gal files. +This project uses GuardLink annotations in source code comments. ## Core Requirement Every time you write or modify code that touches security-relevant behavior, you MUST add GuardLink annotations in the same change. This includes: new endpoints, auth logic, data validation, database queries, file I/O, external API calls, crypto, process spawning, user input handling, config parsing. Do NOT annotate pure business logic, formatting utilities, UI components, or helpers that never touch security boundaries. @@ -9,9 +9,10 @@ Every time you write or modify code that touches security-relevant behavior, you ## Key Rules - ANNOTATE NEW CODE. When you add a function or endpoint that handles user input, accesses data, or crosses a trust boundary — add @exposes, @mitigates, @flows, @handles, or at minimum @comment. This is not optional. - NEVER write @accepts — that is a human-only governance decision. For risks with no mitigation: write @exposes + @audit + @comment suggesting potential controls. +- Use @confirmed for verified exploits. When pentest/scanning/manual reproduction proves a threat is exploitable: @confirmed #threat on Asset [severity] -- "evidence". Distinct from @exposes (theoretical) — @confirmed means real, verified, no false positives. - Preserve existing annotations — do not delete or mangle them. - Definitions (@asset, @threat, @control with (#id)) live in .guardlink/definitions.ts. Reuse IDs — never redefine. Add new definitions there first, then reference in source files. -- Relationship annotations use verbs: @mitigates, @exposes, @flows, @handles, @boundary, @comment, @validates, @audit, @owns, @assumes, @transfers. +- Source files use relationship verbs: @mitigates, @exposes, @confirmed, @flows, @handles, @boundary, @comment, @validates, @audit, @owns, @assumes, @transfers, @feature. - Write coupled annotation blocks: risk + control (or audit) + data flow + context note. - Avoid @shield unless a human explicitly asks to hide code from AI. @@ -30,6 +31,8 @@ Every time you write or modify code that touches security-relevant behavior, you - @handles pii on App.API -- "Processes email, token" - @validates #prepared-stmts for App.API -- "CI test ensures placeholders" - @audit App.API -- "Token rotation review" +- @confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +- @feature "SSO Login" -- "Single sign-on authentication flow" - @owns security-team for App.API -- "Team responsible" - @comment -- "Rate limit: 100 req/15min" @@ -43,33 +46,25 @@ Every time you write or modify code that touches security-relevant behavior, you ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -77,15 +72,29 @@ Every time you write or modify code that touches security-relevant behavior, you - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features @@ -106,6 +115,16 @@ Every time you write or modify code that touches security-relevant behavior, you + + + + + + + + + + diff --git a/.cursor/rules/guardlink.mdc b/.cursor/rules/guardlink.mdc index 7841c07..f8efbbb 100644 --- a/.cursor/rules/guardlink.mdc +++ b/.cursor/rules/guardlink.mdc @@ -6,7 +6,7 @@ alwaysApply: true # GuardLink Security Annotations -This project uses GuardLink annotations in source code comments or standalone .gal files. +This project uses GuardLink annotations in source code comments. ## Core Requirement Every time you write or modify code that touches security-relevant behavior, you MUST add GuardLink annotations in the same change. This includes: new endpoints, auth logic, data validation, database queries, file I/O, external API calls, crypto, process spawning, user input handling, config parsing. Do NOT annotate pure business logic, formatting utilities, UI components, or helpers that never touch security boundaries. @@ -14,9 +14,10 @@ Every time you write or modify code that touches security-relevant behavior, you ## Key Rules - ANNOTATE NEW CODE. When you add a function or endpoint that handles user input, accesses data, or crosses a trust boundary — add @exposes, @mitigates, @flows, @handles, or at minimum @comment. This is not optional. - NEVER write @accepts — that is a human-only governance decision. For risks with no mitigation: write @exposes + @audit + @comment suggesting potential controls. +- Use @confirmed for verified exploits. When pentest/scanning/manual reproduction proves a threat is exploitable: @confirmed #threat on Asset [severity] -- "evidence". Distinct from @exposes (theoretical) — @confirmed means real, verified, no false positives. - Preserve existing annotations — do not delete or mangle them. - Definitions (@asset, @threat, @control with (#id)) live in .guardlink/definitions.ts. Reuse IDs — never redefine. Add new definitions there first, then reference in source files. -- Relationship annotations use verbs: @mitigates, @exposes, @flows, @handles, @boundary, @comment, @validates, @audit, @owns, @assumes, @transfers. +- Source files use relationship verbs: @mitigates, @exposes, @confirmed, @flows, @handles, @boundary, @comment, @validates, @audit, @owns, @assumes, @transfers, @feature. - Write coupled annotation blocks: risk + control (or audit) + data flow + context note. - Avoid @shield unless a human explicitly asks to hide code from AI. @@ -35,6 +36,8 @@ Every time you write or modify code that touches security-relevant behavior, you - @handles pii on App.API -- "Processes email, token" - @validates #prepared-stmts for App.API -- "CI test ensures placeholders" - @audit App.API -- "Token rotation review" +- @confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +- @feature "SSO Login" -- "Single sign-on authentication flow" - @owns security-team for App.API -- "Team responsible" - @comment -- "Rate limit: 100 req/15min" @@ -48,33 +51,25 @@ Every time you write or modify code that touches security-relevant behavior, you ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -82,12 +77,26 @@ Every time you write or modify code that touches security-relevant behavior, you - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features diff --git a/.gemini/GEMINI.md b/.gemini/GEMINI.md index c5ec022..cfe8ef8 100644 --- a/.gemini/GEMINI.md +++ b/.gemini/GEMINI.md @@ -3,7 +3,7 @@ ## GuardLink — Security Annotations (Required) -This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments or standalone `.gal` files. +This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments. **Full reference: `docs/GUARDLINK_REFERENCE.md`** ### Core Requirement @@ -14,11 +14,12 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c 1. **Annotate new code.** When you add a function, endpoint, or module that handles user input, accesses data, crosses a trust boundary, or could fail in a security-relevant way — add `@exposes`, `@mitigates`, `@flows`, `@handles`, or at minimum `@comment` annotations. This is not optional. 2. **NEVER write `@accepts`.** That is a human-only governance decision. When you find a risk with no mitigation in code, write `@exposes` to document the risk + `@audit` to flag it for human review + `@comment` to suggest potential controls. -3. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. -4. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. -5. Relationship annotations use verbs like: `@mitigates`, `@exposes`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`. -6. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. -7. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. +3. **Use `@confirmed` for verified exploits.** When a pentest, CXG scan, or manual reproduction proves a threat is exploitable, mark it with `@confirmed #threat on Asset [severity] -- "evidence"`. This is distinct from `@exposes` (theoretical) — `@confirmed` means real, verified, not a false positive. Include severity based on actual observed impact. +4. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. +5. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. +6. Source files use relationship verbs only: `@mitigates`, `@exposes`, `@confirmed`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`, `@feature`. +7. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. +8. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. ### Workflow (while coding) @@ -43,6 +44,8 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c @handles pii on App.API -- "Processes email and session token" @validates #prepared-stmts for App.API -- "sqlInjectionTest.ts ensures placeholders used" @audit App.API -- "Token rotation logic needs crypto review" +@confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +@feature "SSO Login" -- "Single sign-on authentication flow" @owns security-team for App.API -- "Team responsible for reviews" @comment -- "Rate limit: 100 req/15min via express-rate-limit" ``` @@ -57,33 +60,25 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -91,15 +86,29 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features > **Note:** This section is auto-generated. Run `guardlink sync` to update after code changes. > Any coding agent (Cursor, Claude, Copilot, Windsurf, etc.) should reference these IDs @@ -124,6 +133,16 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c + + + + + + + + + + diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 337ebbc..570624d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,7 +3,7 @@ ## GuardLink — Security Annotations (Required) -This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments or standalone `.gal` files. +This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments. **Full reference: `docs/GUARDLINK_REFERENCE.md`** ### Core Requirement @@ -14,11 +14,12 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c 1. **Annotate new code.** When you add a function, endpoint, or module that handles user input, accesses data, crosses a trust boundary, or could fail in a security-relevant way — add `@exposes`, `@mitigates`, `@flows`, `@handles`, or at minimum `@comment` annotations. This is not optional. 2. **NEVER write `@accepts`.** That is a human-only governance decision. When you find a risk with no mitigation in code, write `@exposes` to document the risk + `@audit` to flag it for human review + `@comment` to suggest potential controls. -3. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. -4. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. -5. Relationship annotations use verbs like: `@mitigates`, `@exposes`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`. -6. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. -7. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. +3. **Use `@confirmed` for verified exploits.** When a pentest, CXG scan, or manual reproduction proves a threat is exploitable, mark it with `@confirmed #threat on Asset [severity] -- "evidence"`. This is distinct from `@exposes` (theoretical) — `@confirmed` means real, verified, not a false positive. Include severity based on actual observed impact. +4. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. +5. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. +6. Source files use relationship verbs only: `@mitigates`, `@exposes`, `@confirmed`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`, `@feature`. +7. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. +8. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. ### Workflow (while coding) @@ -43,6 +44,8 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c @handles pii on App.API -- "Processes email and session token" @validates #prepared-stmts for App.API -- "sqlInjectionTest.ts ensures placeholders used" @audit App.API -- "Token rotation logic needs crypto review" +@confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +@feature "SSO Login" -- "Single sign-on authentication flow" @owns security-team for App.API -- "Team responsible for reviews" @comment -- "Rate limit: 100 req/15min via express-rate-limit" ``` @@ -57,33 +60,25 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -91,15 +86,29 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features > **Note:** This section is auto-generated. Run `guardlink sync` to update after code changes. > Any coding agent (Cursor, Claude, Copilot, Windsurf, etc.) should reference these IDs @@ -125,6 +134,16 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 582eb4e..7f9a03e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,15 @@ pnpm-lock.yaml # GuardLink runtime config (API keys - do not commit) .guardlink/config.json +# Generated outputs at repo root (canonical samples live in docs/examples/). +# Running `guardlink dashboard .` or `guardlink report .` from the repo root +# writes here by default — those local copies should never be committed. +/threat-dashboard.html +/threat-model.md +/threat-model.json +/guardlink-pentest.html +/guardlink-pentest.json +/guardlink-pentest.sarif + # Debug / internal _debug.ts diff --git a/.guardlink/prompt.md b/.guardlink/prompt.md new file mode 100644 index 0000000..3fbc86d --- /dev/null +++ b/.guardlink/prompt.md @@ -0,0 +1,24 @@ +# guardlink — Project Description + + + + +## What This Application Does + + + +## Key Components + + + +## Trust Boundaries + + + +## Data Sensitivity + + + +## Deployment Context + + diff --git a/.windsurfrules b/.windsurfrules index 7fbb9e1..2141dea 100644 --- a/.windsurfrules +++ b/.windsurfrules @@ -1,7 +1,7 @@ # GuardLink Security Annotations -This project uses GuardLink annotations in source code comments or standalone .gal files. +This project uses GuardLink annotations in source code comments. ## Core Requirement Every time you write or modify code that touches security-relevant behavior, you MUST add GuardLink annotations in the same change. This includes: new endpoints, auth logic, data validation, database queries, file I/O, external API calls, crypto, process spawning, user input handling, config parsing. Do NOT annotate pure business logic, formatting utilities, UI components, or helpers that never touch security boundaries. @@ -9,9 +9,10 @@ Every time you write or modify code that touches security-relevant behavior, you ## Key Rules - ANNOTATE NEW CODE. When you add a function or endpoint that handles user input, accesses data, or crosses a trust boundary — add @exposes, @mitigates, @flows, @handles, or at minimum @comment. This is not optional. - NEVER write @accepts — that is a human-only governance decision. For risks with no mitigation: write @exposes + @audit + @comment suggesting potential controls. +- Use @confirmed for verified exploits. When pentest/scanning/manual reproduction proves a threat is exploitable: @confirmed #threat on Asset [severity] -- "evidence". Distinct from @exposes (theoretical) — @confirmed means real, verified, no false positives. - Preserve existing annotations — do not delete or mangle them. - Definitions (@asset, @threat, @control with (#id)) live in .guardlink/definitions.ts. Reuse IDs — never redefine. Add new definitions there first, then reference in source files. -- Relationship annotations use verbs: @mitigates, @exposes, @flows, @handles, @boundary, @comment, @validates, @audit, @owns, @assumes, @transfers. +- Source files use relationship verbs: @mitigates, @exposes, @confirmed, @flows, @handles, @boundary, @comment, @validates, @audit, @owns, @assumes, @transfers, @feature. - Write coupled annotation blocks: risk + control (or audit) + data flow + context note. - Avoid @shield unless a human explicitly asks to hide code from AI. @@ -30,6 +31,8 @@ Every time you write or modify code that touches security-relevant behavior, you - @handles pii on App.API -- "Processes email, token" - @validates #prepared-stmts for App.API -- "CI test ensures placeholders" - @audit App.API -- "Token rotation review" +- @confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +- @feature "SSO Login" -- "Single sign-on authentication flow" - @owns security-team for App.API -- "Team responsible" - @comment -- "Rate limit: 100 req/15min" @@ -43,33 +46,25 @@ Every time you write or modify code that touches security-relevant behavior, you ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -77,15 +72,29 @@ Every time you write or modify code that touches security-relevant behavior, you - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features @@ -106,6 +115,16 @@ Every time you write or modify code that touches security-relevant behavior, you + + + + + + + + + + diff --git a/AGENTS.md b/AGENTS.md index c5ec022..e19e481 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,7 @@ ## GuardLink — Security Annotations (Required) -This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments or standalone `.gal` files. +This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments. **Full reference: `docs/GUARDLINK_REFERENCE.md`** ### Core Requirement @@ -14,11 +14,12 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c 1. **Annotate new code.** When you add a function, endpoint, or module that handles user input, accesses data, crosses a trust boundary, or could fail in a security-relevant way — add `@exposes`, `@mitigates`, `@flows`, `@handles`, or at minimum `@comment` annotations. This is not optional. 2. **NEVER write `@accepts`.** That is a human-only governance decision. When you find a risk with no mitigation in code, write `@exposes` to document the risk + `@audit` to flag it for human review + `@comment` to suggest potential controls. -3. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. -4. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. -5. Relationship annotations use verbs like: `@mitigates`, `@exposes`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`. -6. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. -7. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. +3. **Use `@confirmed` for verified exploits.** When a pentest, CXG scan, or manual reproduction proves a threat is exploitable, mark it with `@confirmed #threat on Asset [severity] -- "evidence"`. This is distinct from `@exposes` (theoretical) — `@confirmed` means real, verified, not a false positive. Include severity based on actual observed impact. +4. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. +5. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. +6. Source files use relationship verbs only: `@mitigates`, `@exposes`, `@confirmed`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`, `@feature`. +7. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. +8. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. ### Workflow (while coding) @@ -43,6 +44,8 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c @handles pii on App.API -- "Processes email and session token" @validates #prepared-stmts for App.API -- "sqlInjectionTest.ts ensures placeholders used" @audit App.API -- "Token rotation logic needs crypto review" +@confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +@feature "SSO Login" -- "Single sign-on authentication flow" @owns security-team for App.API -- "Team responsible for reviews" @comment -- "Rate limit: 100 req/15min via express-rate-limit" ``` @@ -57,33 +60,25 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -91,15 +86,29 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features > **Note:** This section is auto-generated. Run `guardlink sync` to update after code changes. > Any coding agent (Cursor, Claude, Copilot, Windsurf, etc.) should reference these IDs @@ -126,6 +135,26 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c + + + + + + + + + + + + + + + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 19aabe0..9507375 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,86 @@ # Changelog - All notable changes to GuardLink CLI will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## \[1.4.3\] — 2026-05-13 + +### Added + +- **Multi-hop** `@flows` **chains** — `@flows A -> B -> C -> D` is now valid syntax for chains of any length, expanding into N-1 pairwise flows that share the same mechanism, description, and source location. Single-hop syntax (`A -> B`) unchanged. Downstream consumers (DFD, sequence diagram, MCP queries, SARIF) still see the pairwise shape — multi-hop is purely a parser-side expansion. + +- **Quoted asset and threat refs in relationships** — `ASSET_REF` and `THREAT_REF` now accept double-quoted strings as a third alternative alongside `#id` and `Dotted.Path`. Example: `@flows User -> "/rest/user/login" -> "SQLite db"` parses cleanly. Same syntax works in `@exposes`, `@confirmed`, `@boundary`, `@audit`, and other relationship verbs. Definition annotations (`@asset`, `@threat`, `@control`) remain strict — declarations stay on `#id` and dotted paths. + +- **Opt-in pentest evidence redaction** (`guardlink config set redact-evidence true`) — surgical redaction for teams whose compliance posture requires no cleartext credentials at rest. When enabled, JWT signatures are stripped (header + payload preserved as proof of exploit), `Authorization: Basic`/`Digest`/`NTLM` values are fully redacted, credential field values in JSON / query-strings / cookies are masked (field names preserved). Default OFF; OSS users running against test targets see full evidence. Dashboard shows a banner when redaction is active. Full operational guide: [`docs/handling-evidence.md`](docs/handling-evidence.md). + +- `@confirmed` **annotation** — New verb for verified exploitable findings. Distinct from `@exposes` (theoretical) and `@accepts` (governance). Syntax: `@confirmed #threat on Asset [severity] cwe:CWE-NNN -- "evidence"`. A `@confirmed` annotation means the threat has been proven exploitable through pentest, automated CXG scan with reproducible evidence, or manual reproduction — not a false positive. Full pipeline: parser, model assembly, dangling-ref validation, SARIF `error`-level export, CLI `status` output, dashboard emphasis, LLM report inclusion, MCP `guardlink_lookup "confirmed"`. + +- `@feature` **annotation** — New metadata verb to tag files/code with a named product feature. Syntax: `@feature "Feature Name" -- "description"`. Association is file-level: all annotations in a file with `@feature "X"` are considered part of that feature. Enables feature-scoped filtering across all output modes. + +- **Feature filtering (**`--feature` **flag)** — `guardlink status`, `guardlink report`, and `guardlink dashboard` all gain `--feature ` (comma-separated). Filters all output — assets, threats, exposures, flows — to files tagged with the named feature(s). Dashboard gets a live feature filter dropdown in the header with a dismissible banner. TUI gains `/feature [name]` command to list features or drill into one. + +- `guardlink translate [prompt]` — New command that translates GuardLink threat model findings into CERT-X-GEN (CXG) pentest templates (generation only, no execution). Supports all agent backends: `--claude-code`, `--codex`, `--gemini`, `--cursor`, `--windsurf`, `--clipboard`. Reads CXG reference docs and skeleton templates from `GUARDLINK_CXG_ROOT` env or configured default path. + +- `guardlink ask ` — New command that answers natural-language questions about the threat model and codebase context, launching an AI agent with full model serialization as context. + +- **Pentest integration** — GuardLink now loads CXG scan results from `.guardlink/pentest-findings/` (JSON) and template metadata from `.guardlink/cxg-templates/`. New interfaces: `PentestFinding`, `PentestScanResult`, `PentestTemplate`, `PentestData`. Findings are injected as a `` block into AI threat reports, `guardlink threat-report`, and the dashboard. Dashboard gains a dedicated **Pentest Findings** sidebar section with scan summary tables and per-finding detail drawers. + +- **Expanded threat model report** (`guardlink report`) — `generateReport()` now produces 10 structured sections (was: Executive Summary + tables): + + 1. Application Overview (auto-populated from `.guardlink/prompt.md` if present) + 2. Scope of This Threat Model + 3. Architecture (Mermaid DFD) + 4. Key Flows & Sequence (new Mermaid sequence diagram from `@flows`) + 5. Data Inventory + 6. Roles & Access + 7. Dependencies + 8. Secrets, Keys & Credential Management + 9. Logging, Monitoring & Audit + 10. AI/ML System Details (conditional — emitted only when AI-related threats are detected) -## [1.4.2] — 2026-04-24 + Report header now includes GuardLink version and git commit/branch from metadata. Confirmed exploitable findings appear as a row in the Executive Summary table. + +- **Sequence diagram** (`src/report/sequence.ts`) — New Mermaid `sequenceDiagram` generator built from `@flows` annotations, showing step-by-step participant interactions. Used in the Key Flows & Sequence report section. + +- `.guardlink/prompt.md` — `guardlink init` and `guardlink sync` now create this skeleton file. AI annotation agents fill it in with a security-focused project overview (what the app does, components, trust boundaries, data sensitivity, deployment). `guardlink report` reads it and injects the content as the Application Overview section. + +- **SARIF: confirmed exploitable rule** — New `guardlink/confirmed-exploitable` SARIF rule emitting `error`-level results for `@confirmed` annotations. These appear alongside unmitigated exposures in GitHub Advanced Security. + +- **MCP** `guardlink_lookup` **queries** — Two new query types: `"confirmed"` returns all `@confirmed` verified findings; `"features"` returns all `@feature`-tagged feature names with their associated files. + +- **LLM prompt improvements** — `buildUserMessage()` accepts pentest findings context. AI prompts now distinguish pentest-confirmable threats from governance/design gaps, and teach agents when to use `@confirmed` vs `@exposes` vs `@audit`. + +### Changed + +- `guardlink status` — Now prints `@confirmed` findings with a red badge below the exposure list. Accepts `--feature` for filtered output. +- `guardlink report` — Accepts `--feature` for scoped reports. Reads `.guardlink/prompt.md` for Application Overview. +- `guardlink dashboard` — Accepts `--feature`. Risk score formula now accounts for confirmed finding count. Feature filter dropdown in header. +- `guardlink threat-report` — Pentest findings from `.guardlink/pentest-findings/` are automatically included in AI analysis context. AI prompted to emit a dedicated "Pentest Results" section when findings are present. +- `/gal` **TUI command** — Documents `@feature` tagging with examples. +- **SARIF export** — `@confirmed` findings now appear as `error`-level entries under the new rule; `@exposes` severity mapping unchanged. +- **MCP server** — Status tool description updated to reflect confirmed count. `guardlink_lookup` extended with `confirmed` and `features` queries. + +### Fixed + +- **`guardlink report` no longer prints "Fix errors above before generating report"** when diagnostics contain errors — the message was misleading because the report generated anyway. Per-annotation parse errors don't block report generation; affected annotations are skipped while the rest of the model still renders. Behavior now matches `dashboard`, `sarif`, and `threat-report`. +- **MCP `guardlink_lookup` resolver agrees with itself across query types** — `asset #login` previously returned `count: 0` when an identifier was referenced (e.g. via `@confirmed`) but never declared in `definitions.ts`, even though `threats for #login`, `unmitigated`, and `confirmed` all returned the joined record. Bare `#id` queries had the same problem — they returned `no_match` for identifiers other queries happily resolved. Both `lookupAsset()` and `lookupFuzzy()` now fall back to the annotation graph (exposures, confirmed, mitigations, acceptances, audits, flows, boundaries) and synthesize stub records marked `declared: false` with a `referenced_in: [...]` audit trail. Consumers can distinguish synthesized stubs from real declarations. +- **MCP `guardlink_lookup` no_match hint no longer mangles its quotes** — the hint contained literal double-quote characters that got escaped twice through the MCP transport (content wrap + JSON-RPC envelope), rendering as `\\\"asset \\\"` in clients that print the raw response. Hint now uses backticks around examples so it survives both `JSON.stringify` passes intact. +- **Pentest template card titles in the dashboard now show the actual template id** (e.g. `login-sqli-network`) instead of fragments like `ge` or `e`. The previous loader regex `/id[:\s]*["']?([a-z0-9_-]+)["']?/i` matched the substring "id" inside words like `bridge` and `guide`. +- **Pentest template card severity is no longer hardcoded to `medium`** — the loader's severity regex required a colon between the field name and the value, missing Python templates that use `severity = "critical"` (equals separator). Both regexes now anchor on a complete field name with optional surrounding quotes (for JSON `"id": "x"` form) and accept `:` or `=` as the separator before a quoted value. +- **`guardlink status` row labels** — renamed the file-counting rows from `Annotated`/`Not annotated` to `Files annotated`/`Files unannotated`, removing the visual collision with the `Annotations` row directly below. The count of files-with-annotations is no longer easily misread as the total annotation count. +- **Pentest finding confidence renders defensively across CXG output shapes** — the dashboard previously hardcoded `${f.confidence}%`, assuming integer percentage. CXG has emitted confidence as integers, severity-style strings (`"high"`), and missing values across versions; the inline rendering produced `high%`, `undefined%`, and even `[object Object]%`. New `formatConfidence()` helper handles every case, clamps integers to `[0, 100]`, and never throws. The dashboard still shows `50%` for every finding today because CXG itself hardcodes that — a CXG-side fix lands separately; GuardLink will display the correct value when it does. +- **Topology dedupes undeclared refs across kinds** — an undeclared identifier like `#login-sqli` referenced as both an asset (by `@exposes`) and a threat (by `@confirmed`) previously synthesized two separate nodes in different clusters of the force-directed dashboard graph. The alias resolver now does cross-kind dedup before synthesizing; declared assets/threats/controls always take priority. New `declared: boolean` field on topology nodes lets downstream consumers distinguish synthesized stubs from real declarations. +- **Multi-hop** `@flows` **annotations are no longer rejected** — `@flows User -> /api -> DB` previously failed with `Malformed @flows annotation: could not parse arguments` because the regex required exactly two `ASSET_REF` captures separated by a single arrow. See Added section for the new multi-hop syntax. +- **URL-style and whitespace-containing refs work in** `@flows` **and other relationships** — `/rest/user/login`, `"SQLite db"`, `"Auth Service"` now parse where they didn't before. The `ASSET_REF` regex previously accepted only `#id` and `Dotted.Path` forms. See Added section for quoted-ref syntax. +- **`.guardlink/prompt.md` auto-migrates for v1.4.x projects on first** `guardlink report` — projects upgraded from earlier versions didn't have the new file (since `guardlink init` short-circuits when `.guardlink/` exists), causing reports to silently fall back to a boilerplate Application Overview. Now created automatically on first report with a one-line stderr nudge so the user discovers the feature. Existing user content is never overwritten; the operation is idempotent. New `ensurePromptMd()` helper in `src/init/migrate.ts`. + +### Internal + +- **Generated samples moved to `docs/examples/`** — `threat-dashboard.html`, `threat-model.md`, and `guardlink-pentest.{html,json,sarif}` were previously committed at the repo root, where every `guardlink dashboard .` run from the project root rewrote them and produced churn in unrelated PRs. They now live under `docs/examples/` (with a `README.md` documenting how to regenerate them deliberately) and the root paths are git-ignored. +- **`fatal` diagnostic tier reserved** — `ParseDiagnostic.level` extended from `'error' | 'warning'` to `'error' | 'warning' | 'fatal'` with detailed JSDoc explaining tier semantics. No code path currently emits a fatal; this is a non-breaking type widening so v1.6 can introduce the first emission site (for unrecoverable conditions like schema version mismatch or unparseable definitions) without a coordinated cross-file change. New `diagnosticIcon()` helper in `src/parser/format.ts` centralizes the level → icon mapping (`✗✗` / `✗` / `⚠`); CLI and TUI printers use it consistently. A `TODO(fatal-tier)` note in `src/types/index.ts` enumerates the 11 audit sites that need updating before the first emission lands. +- **Test coverage** — new test files: `tests/lookup.test.ts` (14 tests across the MCP query DSL with regression guards for the resolver bugs), `tests/pentest-loader.test.ts` (10 tests covering JSON/Python/YAML conventions for template metadata extraction), `tests/format.test.ts` (9 tests for confidence rendering across number/string/missing inputs), `tests/migrate.test.ts` (5 tests for prompt.md migration outcomes including idempotence), `tests/diagnostics.test.ts` (7 tests covering the fatal-tier vocabulary and icon mapping), `tests/redact.test.ts` (27 tests for surgical evidence redaction including JWT split-redact, Authorization header variants, JSON / query-string / cookie credential patterns, object-key inspection, and safety properties), plus extensions to `tests/parser.test.ts` (+19 tests for multi-hop chains and quoted refs) and `tests/dashboard.test.ts` (+4 tests for cross-kind topology dedup). Suite total: 72 → 167. + +## \[1.4.2\] — 2026-04-24 ### Added @@ -35,15 +110,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Version**: bump from `1.4.1-gal` development tag (landed via #6) to `1.4.2` across `package.json`, `package-lock.json`, `src/cli/index.ts`, and `src/mcp/server.ts`. - **Lockfiles**: remove committed `bun.lock` (landed via #6). This project standardizes on npm; `package-lock.json` is canonical. Added `bun.lock`, `yarn.lock`, and `pnpm-lock.yaml` to `.gitignore` so contributors using alternate package managers locally do not accidentally commit a second lockfile. -## [1.4.1] — 2026-03-12 +## \[1.4.1\] — 2026-03-12 ### Fixed - -- **GAL reference (`/gal`, `guardlink gal`)**: Fixed all syntax examples to match the actual parser — descriptions now correctly show `-- "quoted text"` format instead of the non-functional `: text` format; severity now shows bracket notation `[high]` / `[P0]` instead of `severity:high`; `@flows` now shows `->` arrow syntax instead of `to`; `@validates` now shows `for` preposition instead of `on`; `@owns` now includes the required `for` preposition; `@mitigates` now documents `using` as the primary keyword (with `with` as v1 compat) +- **GAL reference (**`/gal`**,** `guardlink gal`**)**: Fixed all syntax examples to match the actual parser — descriptions now correctly show `-- "quoted text"` format instead of the non-functional `: text` format; severity now shows bracket notation `[high]` / `[P0]` instead of `severity:high`; `@flows` now shows `->` arrow syntax instead of `to`; `@validates` now shows `for` preposition instead of `on`; `@owns` now includes the required `for` preposition; `@mitigates` now documents `using` as the primary keyword (with `with` as v1 compat) - **GAL reference**: Added missing documentation for external references (`cwe:CWE-89`, `owasp:A03:2021`, `capec:CAPEC-66`, `attack:T1190`) on `@threat` and `@exposes` annotations - **GAL reference**: Added missing `@boundary` alternate syntaxes (`@boundary between A and B`, `@boundary A | B`) and `(#id)` support - **GAL reference**: Added missing standalone `@shield` single-line marker (was only documenting `@shield:begin/end` blocks) -- **TUI `/help`**: Added missing `/unannotated` command to the help output (was registered and functional but not listed) +- **TUI** `/help`: Added missing `/unannotated` command to the help output (was registered and functional but not listed) - **CLI version**: Fixed `guardlink --version` reporting `1.1.0` instead of the actual package version ### Changed @@ -53,7 +127,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Annotations**: Changed `@comment` to `@audit` on agent-launcher timeout note for better governance visibility - **Annotations**: Added `@audit` to MCP suggest module, added workspace-related controls to definitions -## [1.4.0] — 2026-02-27 +## \[1.4.0\] — 2026-02-27 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 337ebbc..766e8df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,7 +3,7 @@ ## GuardLink — Security Annotations (Required) -This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments or standalone `.gal` files. +This project uses [GuardLink](https://guardlink.bugb.io) annotations in source code comments. **Full reference: `docs/GUARDLINK_REFERENCE.md`** ### Core Requirement @@ -14,11 +14,12 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c 1. **Annotate new code.** When you add a function, endpoint, or module that handles user input, accesses data, crosses a trust boundary, or could fail in a security-relevant way — add `@exposes`, `@mitigates`, `@flows`, `@handles`, or at minimum `@comment` annotations. This is not optional. 2. **NEVER write `@accepts`.** That is a human-only governance decision. When you find a risk with no mitigation in code, write `@exposes` to document the risk + `@audit` to flag it for human review + `@comment` to suggest potential controls. -3. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. -4. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. -5. Relationship annotations use verbs like: `@mitigates`, `@exposes`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`. -6. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. -7. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. +3. **Use `@confirmed` for verified exploits.** When a pentest, CXG scan, or manual reproduction proves a threat is exploitable, mark it with `@confirmed #threat on Asset [severity] -- "evidence"`. This is distinct from `@exposes` (theoretical) — `@confirmed` means real, verified, not a false positive. Include severity based on actual observed impact. +4. Do not delete or mangle existing annotations. Treat them as part of the code. Edit only when intentionally changing the threat model. +5. Definitions (`@asset`, `@threat`, `@control` with `(#id)`) live in `.guardlink/definitions.ts`. Reuse existing `#id`s — never redefine. If you need a new asset or threat, add the definition there first, then reference it in source files. +6. Source files use relationship verbs only: `@mitigates`, `@exposes`, `@confirmed`, `@flows`, `@handles`, `@boundary`, `@comment`, `@validates`, `@audit`, `@owns`, `@assumes`, `@transfers`, `@feature`. +7. Write coupled annotation blocks that tell a complete story: risk + control (or audit) + data flow + context note. Never write a lone `@exposes` without follow-up. +8. Avoid `@shield` unless a human explicitly asks to hide code from AI — it creates blind spots. ### Workflow (while coding) @@ -43,6 +44,8 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c @handles pii on App.API -- "Processes email and session token" @validates #prepared-stmts for App.API -- "sqlInjectionTest.ts ensures placeholders used" @audit App.API -- "Token rotation logic needs crypto review" +@confirmed #sqli on App.API [critical] cwe:CWE-89 -- "Pentest verified: raw SQL injection via email param" +@feature "SSO Login" -- "Single sign-on authentication flow" @owns security-team for App.API -- "Team responsible for reviews" @comment -- "Rate limit: 100 req/15min via express-rate-limit" ``` @@ -57,33 +60,25 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c ### Open Exposures (need @mitigates or @audit) -- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) -- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) - #agent-launcher exposed to #prompt-injection [medium] (src/agents/launcher.ts:13) - #agent-launcher exposed to #dos [low] (src/agents/launcher.ts:15) - #agent-launcher exposed to #prompt-injection [high] (src/agents/prompts.ts:6) -- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:15) -- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:31) -- #init exposed to #data-exposure [low] (src/init/index.ts:12) +- #agent-launcher exposed to #config-tamper [medium] (src/agents/prompts.ts:10) +- #llm-client exposed to #data-exposure [low] (src/analyze/index.ts:12) +- #llm-client exposed to #prompt-injection [medium] (src/analyze/llm.ts:17) +- #cli exposed to #cmd-injection [critical] (src/cli/index.ts:33) +- #sarif exposed to #data-exposure [low] (src/analyzer/sarif.ts:16) - #mcp exposed to #cmd-injection [high] (src/mcp/index.ts:4) - #mcp exposed to #prompt-injection [medium] (src/mcp/server.ts:30) - #mcp exposed to #data-exposure [medium] (src/mcp/server.ts:34) - #suggest exposed to #dos [low] (src/mcp/suggest.ts:16) +- #init exposed to #data-exposure [low] (src/init/index.ts:12) - #parser exposed to #arbitrary-write [high] (src/parser/clear.ts:8) - #tui exposed to #cmd-injection [high] (src/tui/commands.ts:11) - #tui exposed to #prompt-injection [medium] (src/tui/commands.ts:15) ### Existing Data Flows (extend, don't duplicate) -- ThreatModel -> #llm-client via serializeModel -- ProjectFiles -> #llm-client via readFileSync -- #llm-client -> ReportFile via writeFileSync -- LLMConfig -> #llm-client via chatCompletion -- #llm-client -> LLMProvider via fetch -- LLMProvider -> #llm-client via response -- LLMToolCall -> #llm-client via createToolExecutor -- #llm-client -> NVD via fetch -- ProjectFiles -> #llm-client via readFileSync - EnvVars -> #agent-launcher via process.env - ConfigFile -> #agent-launcher via readFileSync - #agent-launcher -> ConfigFile via writeFileSync @@ -91,15 +86,29 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c - #agent-launcher -> AgentProcess via spawn - AgentProcess -> #agent-launcher via stdout - UserPrompt -> #agent-launcher via buildAnnotatePrompt +- UserPrompt -> #agent-launcher via buildTranslatePrompt +- UserPrompt -> #agent-launcher via buildAskPrompt - ThreatModel -> #agent-launcher via model - #agent-launcher -> AgentPrompt via return -- ThreatModel -> #dashboard via computeStats -- SourceFiles -> #dashboard via readFileSync -- ... and 48 more +- ThreatModel -> #llm-client via serializeModel +- ProjectFiles -> #llm-client via readFileSync +- #llm-client -> ReportFile via writeFileSync +- PentestFindings -> #llm-client via readFileSync +- LLMConfig -> #llm-client via chatCompletion +- #llm-client -> LLMProvider via fetch +- LLMProvider -> #llm-client via response +- LLMToolCall -> #llm-client via createToolExecutor +- #llm-client -> NVD via fetch +- ... and 55 more + +### Features (filter with `--feature`) + +- "Dashboard" +- "MCP Integration" ### Model Stats -289 annotations, 16 assets, 15 threats, 12 controls, 60 exposures, 44 mitigations, 68 flows +310 annotations, 16 assets, 15 threats, 12 controls, 61 exposures, 0 confirmed, 48 mitigations, 75 flows, 2 features > **Note:** This section is auto-generated. Run `guardlink sync` to update after code changes. > Any coding agent (Cursor, Claude, Copilot, Windsurf, etc.) should reference these IDs @@ -126,6 +135,26 @@ This project uses [GuardLink](https://guardlink.bugb.io) annotations in source c + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 6d8aee9..164b6fd 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,8 @@ GuardLink ships an MCP server and behavioral directives for AI coding agents. Af | `guardlink sarif [dir]` | Export unmitigated exposures as SARIF 2.1.0 | | `guardlink threat-report [fw]` | AI threat report (stride/dread/pasta/attacker/rapid/general) | | `guardlink threat-reports` | List saved AI threat reports | +| `guardlink translate [prompt]` | Generate CERT-X-GEN pentest templates from threat model findings | +| `guardlink ask ` | Ask a natural-language question about the threat model and codebase | | `guardlink review [dir]` | Interactive governance review — accept, remediate, or skip unmitigated exposures | | `guardlink review --list` | List reviewable exposures without prompting | | `guardlink clear [dir]` | Remove all annotations from source files (with `--dry-run` preview) | @@ -259,6 +261,8 @@ GuardLink annotations can live in source comments in any language or in standalo | `@control` | Define a security control | `@control WAF (#waf)` | | `@mitigates` | Control protects asset against threat | `@mitigates #api against #sqli using #prepared-stmts` | | `@exposes` | Asset vulnerable to threat | `@exposes #api to #xss [P1]` | +| `@confirmed` | Threat verified exploitable (pentest/scan) | `@confirmed #sqli on #api [critical] -- "Verified in pen test"` | +| `@feature` | Tag code with a product feature name | `@feature "SSO Login" -- "Single sign-on authentication flow"` | | `@accepts` | Risk acknowledged | `@accepts #dos on #api -- "By design"` | | `@transfers` | Risk moved between assets | `@transfers #sqli from #api to #db` | | `@flow` | Data flow between assets | `@flow #api -> #db via "SQL"` | @@ -321,7 +325,33 @@ For workspace setups, GuardLink provides two additional workflow templates: a pe ### SARIF -`guardlink sarif` exports unmitigated exposures as SARIF 2.1.0. Upload to GitHub Advanced Security and every `@exposes` appears as a code scanning alert with file, line, severity, and CWE. +`guardlink sarif` exports unmitigated exposures and `@confirmed` findings as SARIF 2.1.0. Upload to GitHub Advanced Security: unmitigated `@exposes` appear as warnings or errors by severity; `@confirmed` exploitable findings appear as errors. + +### Pentest Integration + +GuardLink bridges threat modeling and penetration testing in both directions. + +**From threat model to pentest templates** — `guardlink translate` reads your `@exposes` annotations and generates CERT-X-GEN (CXG) pentest template stubs targeting the specific threats you've documented. Run it with any agent backend: + +```bash +guardlink translate --claude-code +guardlink translate "focus on injection paths" --clipboard +``` + +**From pentest results back to the threat model** — Drop CXG scan result JSON files into `.guardlink/pentest-findings/`. GuardLink reads them automatically and: +- Injects findings as empirical evidence in `guardlink threat-report` and AI analyses +- Displays a **Pentest Findings** section in `guardlink dashboard` +- Teaches agents to cross-reference scan results against `@exposes` annotations + +**Marking verified findings** — When a pentest or scan proves a threat is exploitable, add `@confirmed` to close the loop: + +```typescript +// @confirmed #sqli on App.API [critical] cwe:CWE-89 -- "CXG scan 2026-04: time-based blind SQLi on /login confirmed" +``` + +`@confirmed` is distinct from `@exposes` (hypothesis) — it means real, verified, not a false positive. + +**Handling evidence safely** — Pentest finding JSON files in `.guardlink/pentest-findings/` and generated templates in `.guardlink/cxg-templates/` often contain live tokens, JWTs, credential payloads, and other replay-enabling material captured from successful exploits. Before running scans against any system you care about, add these directories to your repository's ignore file. GuardLink also supports opt-in surgical redaction (`guardlink config set redact-evidence true`) for enterprise users whose compliance posture requires no cleartext credentials at rest. See [docs/handling-evidence.md](docs/handling-evidence.md) for the full operational guide. --- diff --git a/docs/GUARDLINK_REFERENCE.md b/docs/GUARDLINK_REFERENCE.md index f99d3fc..93d5bd5 100644 --- a/docs/GUARDLINK_REFERENCE.md +++ b/docs/GUARDLINK_REFERENCE.md @@ -12,6 +12,7 @@ DEFINE @asset (#id) -- "description" RELATE @mitigates against <#threat> using <#control> -- "how" @exposes to <#threat> [severity] cwe:CWE-NNN -- "what's wrong" + @confirmed <#threat> on [severity] cwe:CWE-NNN -- "verified evidence" @accepts <#threat> on -- "HUMAN-ONLY — AI agents must use @audit instead" @transfers <#threat> from to -- "who handles it" @@ -26,6 +27,8 @@ LIFECYCLE @handles on @assumes -- "unverified assumption" +METADATA @feature "Feature Name" -- "tag code with a feature for filtering" + COMMENT @comment -- "security-relevant developer note" PROTECT @shield -- "reason" @@ -69,10 +72,12 @@ Use the same GAL syntax without language comment prefixes. Definitions still bel | Writing new endpoint/handler | `@exposes` + `@mitigates` (or `@audit`) + `@flows` + `@comment` — tell the complete story | | New service/component | `@asset` in definitions, then reference in source | | Security gap exists | `@exposes Asset to #threat` + `@audit Asset` | +| Threat verified exploitable | `@confirmed #threat on Asset [severity] -- "pentest/scan evidence"` | | Risk with no fix yet | `@audit Asset` + `@comment` explaining potential controls. NEVER `@accepts`. | | Implementing a fix | `@mitigates Asset against #threat using #control` | | Processing sensitive data | `@handles pii on Asset` | | Proprietary algorithm | `@shield:begin` ... `@shield:end` (only if human requests it) | +| Tagging code to a feature | `@feature "SSO Login" -- "Single sign-on flow"` | | Unsure which annotation | `@comment -- "describe what you see"` | ## CLI Commands @@ -94,6 +99,8 @@ guardlink diff [ref] # Compare threat model against a git ref guardlink threat-report # AI threat report (see frameworks below) guardlink threat-reports # List saved threat reports guardlink annotate [--mode inline|external] # Launch coding agent to add annotations +guardlink translate [prompt] # Generate CERT-X-GEN pentest templates from threat findings +guardlink ask # Ask questions about the threat model and codebase guardlink config # Manage LLM provider / CLI agent configuration # Governance & Maintenance @@ -102,11 +109,18 @@ guardlink review --list [--severity X] # List reviewable exposures without prom guardlink clear [dir] [--dry-run] # Remove all annotations from source files guardlink sync [dir] # Sync agent instruction files with current threat model guardlink unannotated [dir] # List source files with no annotations +guardlink feature list [dir] # List all @feature tags with stats +guardlink feature show # Show threat model for a specific feature # Interactive guardlink tui [dir] # Interactive TUI: slash commands + AI chat guardlink mcp # Start MCP server (stdio) for Claude Code, Cursor, etc. guardlink gal # Display GAL annotation language quick reference + +# Feature filtering (--feature flag on report, dashboard, status, translate) +guardlink report . --feature "SSO Login" # Report filtered to feature +guardlink dashboard . --feature "SSO,Payments" # Dashboard filtered to features +guardlink status . --feature "SSO Login" # Status filtered to feature ``` ## Threat Report Frameworks @@ -169,6 +183,7 @@ Run `guardlink tui` for the interactive terminal interface: /diff [ref] Compare model vs git ref (default: HEAD~1) /sarif [-o file] Export SARIF 2.1.0 /gal GAL annotation language guide +/feature List all @feature tags (freeform text) Chat about your threat model with AI ``` @@ -176,12 +191,12 @@ Run `guardlink tui` for the interactive terminal interface: 1. **@boundary requires TWO assets**: `@boundary between #A and #B` or `@boundary #A | #B`. 2. **@flows is ONE source → ONE target per line**: `@flows -> via `. -3. **@exposes / @mitigates require defined #id refs**: Every `#id` must have a definition in `.guardlink/definitions.*`. -4. **Severity in square brackets**: `[P0]` `[P1]` `[P2]` `[P3]` or `[critical]` `[high]` `[medium]` `[low]`. Goes AFTER the threat ref. +3. **@exposes / @mitigates / @confirmed require defined #id refs**: Every `#id` must have a definition in `.guardlink/definitions.*`. +4. **Severity in square brackets**: `[P0]` `[P1]` `[P2]` `[P3]` or `[critical]` `[high]` `[medium]` `[low]`. Goes AFTER the threat ref on `@exposes`; on `@confirmed` it reflects **verified** impact (optional but recommended). 5. **Descriptions in double quotes after --**: `-- "description text here"`. 6. **IDs use parentheses in definitions, hash in references**: Define `(#sqli)`, reference `#sqli`. 7. **Asset references**: Use `#id` or `Dotted.Path` — no spaces or special chars. -8. **External refs space-separated after severity**: `cwe:CWE-89 owasp:A03:2021 capec:CAPEC-66`. +8. **External refs space-separated after severity**: `cwe:CWE-89 owasp:A03:2021 capec:CAPEC-66` (on `@threat`, `@exposes`, `@confirmed`). 9. **@comment always needs -- and quotes**: `@comment -- "your note here"`. 10. **One annotation per comment line.** Do NOT put two @verbs on the same line. @@ -189,7 +204,7 @@ Run `guardlink tui` for the interactive terminal interface: When connected via `.mcp.json`, use: - `guardlink_parse` — parse annotations, return threat model -- `guardlink_lookup` — query threats, controls, exposures by ID +- `guardlink_lookup` — query threats, controls, exposures by ID (try `unmitigated`, `confirmed`) - `guardlink_suggest` — get annotation suggestions for a file - `guardlink_validate` — check for syntax errors - `guardlink_status` — coverage stats diff --git a/docs/SPEC.md b/docs/SPEC.md index 9fb9752..33d3b12 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -24,7 +24,7 @@ GuardLink extends the original ThreatSpec specification (dormant since 2020) wit **1.3. Annotations are structured data.** While readable as English, every annotation has a deterministic grammar that can be parsed by regex into typed data structures. Ambiguous or free-form syntax is avoided. -**1.4. Annotations capture intent, not implementation.** An `@exposes` annotation says "this code is vulnerable to X" — it does not describe how the vulnerability works. A `@mitigates` annotation says "this code defends against X" — it does not duplicate the implementation. The annotation is a pointer from code to the threat model. +**1.4. Annotations capture intent, not implementation.** An `@exposes` annotation says "this code is vulnerable to X" — it does not describe how the vulnerability works. A `@mitigates` annotation says "this code defends against X" — it does not duplicate the implementation. A `@confirmed` annotation says "this threat was verified exploitable here" with evidence summarized in the description — it is not a false positive. The annotation is a pointer from code to the threat model. **1.5. Annotations are incremental.** A codebase does not need 100% annotation coverage to be useful. A single `@exposes` annotation on a critical endpoint is valuable. Tools should work with partial annotation and measure coverage over time. @@ -347,6 +347,40 @@ def get_user(user_id: int): return db.get_user(user_id) # Anyone can access any user ``` +#### `@confirmed` — Verified Exploitable Finding + +``` +@confirmed on [] [] [-- ""] +``` + +Declares that a threat against an asset has been **verified** through penetration testing, automated security scanning with reproducible evidence, or manual exploitation — not a theoretical risk. This is distinct from `@exposes` (developer hypothesis) and from `@accepts` (governance decision to live with risk). Tools should surface `@confirmed` as highest-priority findings (e.g., SARIF `error`, dashboard emphasis). + +The optional `[severity]` reflects **observed** impact from verification; it may differ from the severity on a matching `@exposes` or `@threat` definition. + +```typescript +// @confirmed #secret-exposure on App.Config [critical] cwe:CWE-798 -- "TruffleHog + manual: live API key in committed .env.example, verified against provider" +``` + +#### `@feature` — Feature Tag + +``` +@feature "" [-- ""] +``` + +Tags the file with a named product feature. All annotations in a file that carries `@feature "X"` are associated with that feature. A file may carry multiple `@feature` tags. Feature names are free-form quoted strings. + +Feature tags are metadata only — they have no effect on validation or coverage scoring. Their purpose is to allow threat model consumers (CLI, dashboard, reports) to filter or scope the model to a subset of the system: + +- `guardlink status . --feature "SSO Login"` — coverage stats for one feature +- `guardlink report . --feature "Payments"` — report scoped to that feature +- Dashboard feature filter dropdown + +```typescript +// @feature "SSO Login" -- "Single sign-on authentication flow" +// @feature "Payment Processing" +export async function handleOAuthCallback(req: Request) { ... } +``` + #### `@accepts` — Acknowledge a Risk ``` diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 0000000..17414ec --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,44 @@ +# GuardLink output examples + +These files are sample outputs generated by running GuardLink against its own +codebase. They serve as concrete reference material for the formats GuardLink +produces and as visible dogfood evidence that the tool works on a real +TypeScript project. + +| File | Generated by | What it shows | +|---|---|---| +| `threat-model.md` | `guardlink report .` | Markdown threat model report — the canonical narrative output (Application Overview → Confirmed Exploitable → Audit Items). | +| `threat-dashboard.html` | `guardlink dashboard .` | Self-contained interactive HTML dashboard — Executive Summary, Topology graph, Pentest Findings sidebar, severity breakdown, attack-surface view. | +| `guardlink-pentest.json` | `cxg scan ... --output-format json` | Machine-readable scan results (findings, evidence, scan metadata) ingested by the dashboard's Pentest Findings tab. | +| `guardlink-pentest.sarif` | `cxg scan ... --output-format sarif` | SARIF 2.1.0 export — the format GitHub Advanced Security and most CI security tools consume. | +| `guardlink-pentest.html` | `cxg scan ... --output-format html` | Human-readable scan report from CXG. | + +## How these are produced + +These are NOT hand-edited. To regenerate from a fresh clone: + +```bash +npm install && npm run build && npm link + +# Threat model + dashboard from GuardLink's own annotations +guardlink report . -o docs/examples/threat-model.md +guardlink dashboard . -o docs/examples/threat-dashboard.html + +# Pentest scan (requires CXG installed and templates in .guardlink/cxg-templates/) +cxg scan --scope local://. \ + --template-dir .guardlink/cxg-templates/ \ + --output docs/examples/guardlink-pentest \ + --output-format json,sarif,html +``` + +## Why they live here, not at the repo root + +Earlier versions of v1.5.0 committed these files at the repo root, where +every `guardlink dashboard .` run would overwrite them and create a noisy +git diff. They're now under `docs/examples/` — out of the working +directory tools default to — and the repo root paths (`threat-dashboard.html`, +`threat-model.md`, `guardlink-pentest.*`) are git-ignored so accidental +local regeneration doesn't touch tracked files. + +Update them deliberately when the tool's output format changes +materially, not on every push. diff --git a/docs/examples/guardlink-pentest.html b/docs/examples/guardlink-pentest.html new file mode 100644 index 0000000..58088d1 --- /dev/null +++ b/docs/examples/guardlink-pentest.html @@ -0,0 +1,979 @@ + + + + + + CERT-X-GEN Security Scan Report + + + +
+ +
+

🛡️ CERT-X-GEN Security Scan Report

+

Scan ID: 60be8e79-14f7-4040-afd2-ebf9fad1853e

+
+ 📅 2026-03-31 20:03:16 UTC + ⏱️ 30.00s duration + 🔧 v1.1.0 +
+
+ +
+
+
1
+
Targets Scanned
+
+
+
13
+
Templates Executed
+
+
+
9
+
Total Findings
+
+
+
30.00s
+
Duration
+
+
+ +
+
+
0
+
Critical
+
+
+
4
+
High
+
+
+
2
+
Medium
+
+
+
3
+
Low
+
+
+
0
+
Info
+
+
+ +
+

📋 Findings

+
+ +
+
+

Full Threat Model Exposed via MCP guardlink_parse

+ medium +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-data-exposure-mcp
+
+
+
Confidence
+
80%
+
+
+
Timestamp
+
2026-03-31 20:03:19 UTC
+
+ +
+
Vulnerability IDs
+
CWE-200
+
+
+
+ Description: The MCP guardlink_parse tool returns the complete threat model including: exposures (62 entries), mitigations (44 entries), data flows (71 entries), absolute paths (3 found). Any MCP client connected to the server has full visibility into the project's security posture, including known vulnerabilities and their locations. +
+ + +
+
+ Evidence + Raw output +
+

+                    
+ +
+
+ +
+
+

Unmitigated Vulnerability Details Exposed via MCP

+ medium +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-data-exposure-mcp
+
+
+
Confidence
+
80%
+
+
+
Timestamp
+
2026-03-31 20:03:19 UTC
+
+ +
+
Vulnerability IDs
+
CWE-200
+
+
+
+ Description: The MCP guardlink_status tool reveals 17 unmitigated exposures (5 critical/high severity) with exact file paths and line numbers. This provides a detailed attack roadmap to any connected MCP client. +
+ + +
+
+ Evidence + Raw output +
+

+                    
+ +
+
+ +
+
+

MCP Resource Accessible Without Auth: guardlink://model

+ low +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-data-exposure-mcp
+
+
+
Confidence
+
80%
+
+
+
Timestamp
+
2026-03-31 20:03:19 UTC
+
+ +
+
Vulnerability IDs
+
CWE-200
+
+
+
+ Description: The MCP resource guardlink://model (Full threat model) is accessible without any authentication or authorization check. Response size: 79717 chars. +
+ + +
+
+ Evidence + Raw output +
+
{
+  "version": "1.1.0",
+  "project": "unknown",
+  "generated_at": "2026-03-31T20:03:18.347Z",
+  "source_files": 76,
+  "annotations_parsed": 297,
+  "annotated_files": [
+    ".cxg-test-symlink/external.ts",
+    ".guardlink/definitions.ts",
+    "src/agents/config.ts",
+    "src/agents/index.ts",
+    "sr
+
+ +
+
+ +
+
+

MCP Resource Accessible Without Auth: guardlink://definitions

+ low +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-data-exposure-mcp
+
+
+
Confidence
+
80%
+
+
+
Timestamp
+
2026-03-31 20:03:19 UTC
+
+ +
+
Vulnerability IDs
+
CWE-200
+
+
+
+ Description: The MCP resource guardlink://definitions (Asset/threat/control definitions) is accessible without any authentication or authorization check. Response size: 7384 chars. +
+ + +
+
+ Evidence + Raw output +
+
{
+  "assets": [
+    {
+      "id": "parser",
+      "path": "GuardLink.Parser",
+      "description": "Reads source files from disk, extracts security annotations using regex patterns"
+    },
+    {
+      "id": "cli",
+      "path": "GuardLink.CLI",
+      "description": "Command-line interface, handles u
+
+ +
+
+ +
+
+

MCP Resource Accessible Without Auth: guardlink://unmitigated

+ low +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-data-exposure-mcp
+
+
+
Confidence
+
80%
+
+
+
Timestamp
+
2026-03-31 20:03:19 UTC
+
+ +
+
Vulnerability IDs
+
CWE-200
+
+
+
+ Description: The MCP resource guardlink://unmitigated (Unmitigated exposure list) is accessible without any authentication or authorization check. Response size: 2361 chars. +
+ + +
+
+ Evidence + Raw output +
+
[
+  {
+    "asset": "App",
+    "threat": "#sqli",
+    "file": ".cxg-test-symlink/external.ts",
+    "line": 1
+  },
+  {
+    "asset": "#llm-client",
+    "threat": "#data-exposure",
+    "severity": "low",
+    "file": "src/analyze/index.ts",
+    "line": 12
+  },
+  {
+    "asset": "#llm-client",
+    "threat"
+
+ +
+
+ +
+
+

MCP Path Traversal via root Parameter (guardlink_parse)

+ high +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-path-traversal-mcp
+
+
+
Confidence
+
85%
+
+
+
Timestamp
+
2026-03-31 20:03:20 UTC
+
+ +
+
Vulnerability IDs
+
CWE-22
+
+
+
+ Description: The MCP server accepted root='/etc' and returned data from outside the project directory. An MCP client can read arbitrary directory contents through the parse tool. +
+ + +
+
+ Evidence + Raw output +
+
EACCES: permission denied, scandir '/etc/cups/certs'
+
+ +
+
+ +
+
+

MCP Path Traversal via root Parameter (guardlink_status)

+ high +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-path-traversal-mcp
+
+
+
Confidence
+
85%
+
+
+
Timestamp
+
2026-03-31 20:03:20 UTC
+
+ +
+
Vulnerability IDs
+
CWE-22
+
+
+
+ Description: The MCP server accepted root='/etc' in guardlink_status and returned status data for files outside the project. +
+ + +
+
+ Evidence + Raw output +
+
EACCES: permission denied, scandir '/etc/cups/certs'
+
+ +
+
+ +
+
+

MCP Arbitrary File Write via Report Output Path

+ high +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-path-traversal-mcp
+
+
+
Confidence
+
85%
+
+
+
Timestamp
+
2026-03-31 20:03:20 UTC
+
+ +
+
Vulnerability IDs
+
CWE-22
+
+
+
+ Description: The MCP guardlink_report tool wrote a file to /var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md outside the project root by manipulating the output parameter. +
+ + +
+
+ Evidence + Raw output +
+

+                    
+ +
+
+ +
+
+

Symlink Escape in guardlink clear

+ high +
+
+
+
+
Target
+
/path/to/guardlink-sample
+
+
+
Template
+
guardlink-parser-arbitrary-write
+
+
+
Confidence
+
80%
+
+
+
Timestamp
+
2026-03-31 20:03:46 UTC
+
+ +
+
Vulnerability IDs
+
CWE-73
+
+
+
+ Description: The 'guardlink clear' command follows symlinks and would modify files outside the project root. External file '/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/cxg-arb-write-7lyia0j6/external.ts' was included in the clear operation via a symlink. +
+ + +
+
+ Evidence + Raw output +
+
+Found 294 annotation line(s) across 39 file(s):
+
+  .cxg-test-symlink/external.ts  (1 line)
+  src/agents/config.ts  (10 lines)
+  src/agents/index.ts  (2 lines)
+  src/agents/launcher.ts  (11 lines)
+  src/agents/prompts.ts  (62 lines)
+  src/analyzer/index.ts  (2 lines)
+  src/analyzer/sarif.ts  (5 lines)
+  src/cli/index.ts  (12 lines)
+  src/dashboard/generate.ts  (8 lines)
+  src/dashboard/index.ts  (4 lines)
+  src/diff/git.ts  (10 lines)
+  src/diff/index.ts  (3 lines)
+  src/init/detect.ts  (4 lines)
+  src/init/index.ts  (10 lines)
+  src/analyze/index.ts  (10 lines)
+  src/analyze/llm.ts  (11 lines)
+  src/analyze/prompts.ts  (2 lines)
+  src/analyze/tools.ts  (10 lines)
+  src/mcp/index.ts  (4 lines)
+  src/mcp/lookup.ts  (4 lines)
+  src/mcp/server.ts  (16 lines)
+  src/mcp/suggest.ts  (9 lines)
+  src/parser/clear.ts  (7 lines)
+  src/parser/parse-file.ts  (5 lines)
+  src/parser/parse-line.ts  (3 lines)
+  src/parser/parse-project.ts  (7 lines)
+  src/report/index.ts  (2 lines)
+  src/report/report
+
+ +
+
+ +
+ + +
+ + diff --git a/docs/examples/guardlink-pentest.json b/docs/examples/guardlink-pentest.json new file mode 100644 index 0000000..f631566 --- /dev/null +++ b/docs/examples/guardlink-pentest.json @@ -0,0 +1,290 @@ +{ + "scan_id": "60be8e79-14f7-4040-afd2-ebf9fad1853e", + "started_at": "2026-03-31T20:03:16.873740Z", + "completed_at": "2026-03-31T20:03:46.877445Z", + "findings": [ + { + "id": "0093d7e0-13fb-4a0e-a836-6e8d08a3e7e8", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-data-exposure-mcp", + "severity": "medium", + "confidence": 80, + "title": "Full Threat Model Exposed via MCP guardlink_parse", + "description": "The MCP guardlink_parse tool returns the complete threat model including: exposures (62 entries), mitigations (44 entries), data flows (71 entries), absolute paths (3 found). Any MCP client connected to the server has full visibility into the project's security posture, including known vulnerabilities and their locations.", + "evidence": { + "request": "guardlink_parse", + "response": "", + "matched_patterns": [], + "data": { + "tool": "guardlink_parse", + "model_size_chars": "79717", + "sensitive_fields": "[\"exposures (62 entries)\", \"mitigations (44 entries)\", \"data flows (71 entries)\", \"absolute paths (3 found)\"]", + "abs_paths_sample": "[\"\\\"/report, /sarif, /dashboard write files\\\"\", \"\\\"/annotate and /threat-report spawn child processes\\\"\", \"\\\"/model handles API key input and storage\\\"\"]" + }, + "timestamp": "2026-03-31T20:03:19.090615Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-200" + ], + "cvss_score": null, + "remediation": "Consider access control for MCP tool calls. Redact absolute file paths from MCP responses. Implement scoped access where clients only see relevant subsets.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:19.090616Z" + }, + { + "id": "bb85be3d-0253-4788-aadd-f800d7a91af0", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-data-exposure-mcp", + "severity": "medium", + "confidence": 80, + "title": "Unmitigated Vulnerability Details Exposed via MCP", + "description": "The MCP guardlink_status tool reveals 17 unmitigated exposures (5 critical/high severity) with exact file paths and line numbers. This provides a detailed attack roadmap to any connected MCP client.", + "evidence": { + "request": "guardlink_status", + "response": "", + "matched_patterns": [], + "data": { + "total_unmitigated": "17", + "critical_high": "5", + "sample": "[{\"asset\": \"App\", \"threat\": \"#sqli\", \"file\": \".cxg-test-symlink/external.ts\", \"line\": 1}, {\"asset\": \"#llm-client\", \"threat\": \"#data-exposure\", \"severity\": \"low\", \"file\": \"src/analyze/index.ts\", \"line\": 12}, {\"asset\": \"#llm-client\", \"threat\": \"#prompt-injection\", \"severity\": \"medium\", \"file\": \"src/analyze/llm.ts\", \"line\": 17}]", + "tool": "guardlink_status" + }, + "timestamp": "2026-03-31T20:03:19.090618Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-200" + ], + "cvss_score": null, + "remediation": "Implement MCP client authentication/authorization. Redact sensitive details (absolute paths, severity, exact locations) from MCP responses unless the client is trusted. Consider a read-only mode that omits vulnerability details.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:19.090618Z" + }, + { + "id": "2e777b8a-73c6-4352-b86d-efe7fe3f3770", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-data-exposure-mcp", + "severity": "low", + "confidence": 80, + "title": "MCP Resource Accessible Without Auth: guardlink://model", + "description": "The MCP resource guardlink://model (Full threat model) is accessible without any authentication or authorization check. Response size: 79717 chars.", + "evidence": { + "request": "guardlink://model", + "response": "{\n \"version\": \"1.1.0\",\n \"project\": \"unknown\",\n \"generated_at\": \"2026-03-31T20:03:18.347Z\",\n \"source_files\": 76,\n \"annotations_parsed\": 297,\n \"annotated_files\": [\n \".cxg-test-symlink/external.ts\",\n \".guardlink/definitions.ts\",\n \"src/agents/config.ts\",\n \"src/agents/index.ts\",\n \"sr", + "matched_patterns": [], + "data": { + "description": "Full threat model", + "response_size": "79717", + "resource_uri": "guardlink://model", + "content_snippet": "{\n \"version\": \"1.1.0\",\n \"project\": \"unknown\",\n \"generated_at\": \"2026-03-31T20:03:18.347Z\",\n \"source_files\": 76,\n \"annotations_parsed\": 297,\n \"annotated_files\": [\n \".cxg-test-symlink/external.ts\",\n \".guardlink/definitions.ts\",\n \"src/agents/config.ts\",\n \"src/agents/index.ts\",\n \"sr" + }, + "timestamp": "2026-03-31T20:03:19.090626Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-200" + ], + "cvss_score": null, + "remediation": "Implement MCP client authentication/authorization. Redact sensitive details (absolute paths, severity, exact locations) from MCP responses unless the client is trusted. Consider a read-only mode that omits vulnerability details.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:19.090626Z" + }, + { + "id": "daa0e139-ae7f-44ca-bfdd-3fcdb2ce810e", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-data-exposure-mcp", + "severity": "low", + "confidence": 80, + "title": "MCP Resource Accessible Without Auth: guardlink://definitions", + "description": "The MCP resource guardlink://definitions (Asset/threat/control definitions) is accessible without any authentication or authorization check. Response size: 7384 chars.", + "evidence": { + "request": "guardlink://definitions", + "response": "{\n \"assets\": [\n {\n \"id\": \"parser\",\n \"path\": \"GuardLink.Parser\",\n \"description\": \"Reads source files from disk, extracts security annotations using regex patterns\"\n },\n {\n \"id\": \"cli\",\n \"path\": \"GuardLink.CLI\",\n \"description\": \"Command-line interface, handles u", + "matched_patterns": [], + "data": { + "description": "Asset/threat/control definitions", + "content_snippet": "{\n \"assets\": [\n {\n \"id\": \"parser\",\n \"path\": \"GuardLink.Parser\",\n \"description\": \"Reads source files from disk, extracts security annotations using regex patterns\"\n },\n {\n \"id\": \"cli\",\n \"path\": \"GuardLink.CLI\",\n \"description\": \"Command-line interface, handles u", + "response_size": "7384", + "resource_uri": "guardlink://definitions" + }, + "timestamp": "2026-03-31T20:03:19.090627Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-200" + ], + "cvss_score": null, + "remediation": "Implement MCP client authentication/authorization. Redact sensitive details (absolute paths, severity, exact locations) from MCP responses unless the client is trusted. Consider a read-only mode that omits vulnerability details.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:19.090627Z" + }, + { + "id": "60fee1fd-94f1-49c5-a2b6-af1d4eab9a69", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-data-exposure-mcp", + "severity": "low", + "confidence": 80, + "title": "MCP Resource Accessible Without Auth: guardlink://unmitigated", + "description": "The MCP resource guardlink://unmitigated (Unmitigated exposure list) is accessible without any authentication or authorization check. Response size: 2361 chars.", + "evidence": { + "request": "guardlink://unmitigated", + "response": "[\n {\n \"asset\": \"App\",\n \"threat\": \"#sqli\",\n \"file\": \".cxg-test-symlink/external.ts\",\n \"line\": 1\n },\n {\n \"asset\": \"#llm-client\",\n \"threat\": \"#data-exposure\",\n \"severity\": \"low\",\n \"file\": \"src/analyze/index.ts\",\n \"line\": 12\n },\n {\n \"asset\": \"#llm-client\",\n \"threat\"", + "matched_patterns": [], + "data": { + "response_size": "2361", + "resource_uri": "guardlink://unmitigated", + "content_snippet": "[\n {\n \"asset\": \"App\",\n \"threat\": \"#sqli\",\n \"file\": \".cxg-test-symlink/external.ts\",\n \"line\": 1\n },\n {\n \"asset\": \"#llm-client\",\n \"threat\": \"#data-exposure\",\n \"severity\": \"low\",\n \"file\": \"src/analyze/index.ts\",\n \"line\": 12\n },\n {\n \"asset\": \"#llm-client\",\n \"threat\"", + "description": "Unmitigated exposure list" + }, + "timestamp": "2026-03-31T20:03:19.090629Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-200" + ], + "cvss_score": null, + "remediation": "Implement MCP client authentication/authorization. Redact sensitive details (absolute paths, severity, exact locations) from MCP responses unless the client is trusted. Consider a read-only mode that omits vulnerability details.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:19.090629Z" + }, + { + "id": "095476cd-ca1d-49f9-b03d-412167ca080f", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-path-traversal-mcp", + "severity": "high", + "confidence": 85, + "title": "MCP Path Traversal via root Parameter (guardlink_parse)", + "description": "The MCP server accepted root='/etc' and returned data from outside the project directory. An MCP client can read arbitrary directory contents through the parse tool.", + "evidence": { + "request": "{\"tool\": \"guardlink_parse\", \"traversal_root\": \"/etc\", \"response_snippet\": \"EACCES: permission denied, scandir '/etc/cups/certs'\"}", + "response": "EACCES: permission denied, scandir '/etc/cups/certs'", + "matched_patterns": [], + "data": { + "response_snippet": "EACCES: permission denied, scandir '/etc/cups/certs'", + "traversal_root": "/etc", + "tool": "guardlink_parse" + }, + "timestamp": "2026-03-31T20:03:20.214651Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-22" + ], + "cvss_score": null, + "remediation": "Validate and canonicalize the root parameter using realpath(). Reject paths that resolve outside the expected project directory. Apply allowlist-based path validation at the MCP tool boundary.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:20.214653Z" + }, + { + "id": "def6712b-bbd9-447c-8705-4ccf67d7cb19", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-path-traversal-mcp", + "severity": "high", + "confidence": 85, + "title": "MCP Path Traversal via root Parameter (guardlink_status)", + "description": "The MCP server accepted root='/etc' in guardlink_status and returned status data for files outside the project.", + "evidence": { + "request": "{\"tool\": \"guardlink_status\", \"traversal_root\": \"/etc\", \"response_snippet\": \"EACCES: permission denied, scandir '/etc/cups/certs'\"}", + "response": "EACCES: permission denied, scandir '/etc/cups/certs'", + "matched_patterns": [], + "data": { + "traversal_root": "/etc", + "tool": "guardlink_status", + "response_snippet": "EACCES: permission denied, scandir '/etc/cups/certs'" + }, + "timestamp": "2026-03-31T20:03:20.214687Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-22" + ], + "cvss_score": null, + "remediation": "Validate and canonicalize the root parameter using realpath(). Reject paths that resolve outside the expected project directory. Apply allowlist-based path validation at the MCP tool boundary.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:20.214687Z" + }, + { + "id": "5edab871-57b8-4909-ac0d-36df8cdce448", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-path-traversal-mcp", + "severity": "high", + "confidence": 85, + "title": "MCP Arbitrary File Write via Report Output Path", + "description": "The MCP guardlink_report tool wrote a file to /var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md outside the project root by manipulating the output parameter.", + "evidence": { + "request": "{\"tool\": \"guardlink_report\", \"output_path\": \"../../../../var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md\", \"file_created\": \"/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md\"}", + "response": "", + "matched_patterns": [], + "data": { + "tool": "guardlink_report", + "output_path": "../../../../var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md", + "file_created": "/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md" + }, + "timestamp": "2026-03-31T20:03:20.214690Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-22" + ], + "cvss_score": null, + "remediation": "Validate and canonicalize the root parameter using realpath(). Reject paths that resolve outside the expected project directory. Apply allowlist-based path validation at the MCP tool boundary.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:20.214690Z" + }, + { + "id": "e8186cef-bd63-47f9-aa14-d7f9f792ec7e", + "target": "/path/to/guardlink-sample", + "template_id": "guardlink-parser-arbitrary-write", + "severity": "high", + "confidence": 80, + "title": "Symlink Escape in guardlink clear", + "description": "The 'guardlink clear' command follows symlinks and would modify files outside the project root. External file '/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/cxg-arb-write-7lyia0j6/external.ts' was included in the clear operation via a symlink.", + "evidence": { + "request": "{\"symlink\": \"/path/to/guardlink-sample/.cxg-test-symlink\", \"external_target\": \"/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/cxg-arb-write-7lyia0j6\"}", + "response": "\nFound 294 annotation line(s) across 39 file(s):\n\n .cxg-test-symlink/external.ts (1 line)\n src/agents/config.ts (10 lines)\n src/agents/index.ts (2 lines)\n src/agents/launcher.ts (11 lines)\n src/agents/prompts.ts (62 lines)\n src/analyzer/index.ts (2 lines)\n src/analyzer/sarif.ts (5 lines)\n src/cli/index.ts (12 lines)\n src/dashboard/generate.ts (8 lines)\n src/dashboard/index.ts (4 lines)\n src/diff/git.ts (10 lines)\n src/diff/index.ts (3 lines)\n src/init/detect.ts (4 lines)\n src/init/index.ts (10 lines)\n src/analyze/index.ts (10 lines)\n src/analyze/llm.ts (11 lines)\n src/analyze/prompts.ts (2 lines)\n src/analyze/tools.ts (10 lines)\n src/mcp/index.ts (4 lines)\n src/mcp/lookup.ts (4 lines)\n src/mcp/server.ts (16 lines)\n src/mcp/suggest.ts (9 lines)\n src/parser/clear.ts (7 lines)\n src/parser/parse-file.ts (5 lines)\n src/parser/parse-line.ts (3 lines)\n src/parser/parse-project.ts (7 lines)\n src/report/index.ts (2 lines)\n src/report/report", + "matched_patterns": [], + "data": { + "external_target": "/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/cxg-arb-write-7lyia0j6", + "output_excerpt": "\nFound 294 annotation line(s) across 39 file(s):\n\n .cxg-test-symlink/external.ts (1 line)\n src/agents/config.ts (10 lines)\n src/agents/index.ts (2 lines)\n src/agents/launcher.ts (11 lines)\n src/agents/prompts.ts (62 lines)\n src/analyzer/index.ts (2 lines)\n src/analyzer/sarif.ts (5 lines)\n src/cli/index.ts (12 lines)\n src/dashboard/generate.ts (8 lines)\n src/dashboard/index.ts (4 lines)\n src/diff/git.ts (10 lines)\n src/diff/index.ts (3 lines)\n src/init/detect.ts (4 lines)\n src/init/index.ts (10 lines)\n src/analyze/index.ts (10 lines)\n src/analyze/llm.ts (11 lines)\n src/analyze/prompts.ts (2 lines)\n src/analyze/tools.ts (10 lines)\n src/mcp/index.ts (4 lines)\n src/mcp/lookup.ts (4 lines)\n src/mcp/server.ts (16 lines)\n src/mcp/suggest.ts (9 lines)\n src/parser/clear.ts (7 lines)\n src/parser/parse-file.ts (5 lines)\n src/parser/parse-line.ts (3 lines)\n src/parser/parse-project.ts (7 lines)\n src/report/index.ts (2 lines)\n src/report/report", + "symlink": "/path/to/guardlink-sample/.cxg-test-symlink" + }, + "timestamp": "2026-03-31T20:03:46.871903Z" + }, + "cve_ids": [], + "cwe_ids": [ + "CWE-73" + ], + "cvss_score": null, + "remediation": "1. Resolve and canonicalize all paths with realpath() before operations.\n2. Verify resolved paths are within the project root using startsWith().\n3. Add followSymlinks: false to fast-glob options.\n4. Reject root/dir arguments that resolve outside the expected project boundary.", + "references": [], + "tags": [], + "timestamp": "2026-03-31T20:03:46.871907Z" + } + ], + "statistics": { + "targets_scanned": 1, + "templates_executed": 13, + "findings_by_severity": { + "medium": 2, + "high": 4, + "low": 3 + }, + "network_requests": 0, + "data_transferred": 0, + "duration": { + "secs": 30, + "nanos": 3705000 + }, + "success_rate": 0.6923076923076923 + }, + "errors": [] +} \ No newline at end of file diff --git a/docs/examples/guardlink-pentest.sarif b/docs/examples/guardlink-pentest.sarif new file mode 100644 index 0000000..253a7c7 --- /dev/null +++ b/docs/examples/guardlink-pentest.sarif @@ -0,0 +1,233 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "runs": [ + { + "results": [ + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP guardlink_parse tool returns the complete threat model including: exposures (62 entries), mitigations (44 entries), data flows (71 entries), absolute paths (3 found). Any MCP client connected to the server has full visibility into the project's security posture, including known vulnerabilities and their locations." + }, + "properties": { + "confidence": 80, + "cveIds": [], + "cweIds": [ + "CWE-200" + ], + "severity": "medium" + }, + "ruleId": "guardlink-data-exposure-mcp" + }, + { + "level": "warning", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP guardlink_status tool reveals 17 unmitigated exposures (5 critical/high severity) with exact file paths and line numbers. This provides a detailed attack roadmap to any connected MCP client." + }, + "properties": { + "confidence": 80, + "cveIds": [], + "cweIds": [ + "CWE-200" + ], + "severity": "medium" + }, + "ruleId": "guardlink-data-exposure-mcp" + }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP resource guardlink://model (Full threat model) is accessible without any authentication or authorization check. Response size: 79717 chars." + }, + "properties": { + "confidence": 80, + "cveIds": [], + "cweIds": [ + "CWE-200" + ], + "severity": "low" + }, + "ruleId": "guardlink-data-exposure-mcp" + }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP resource guardlink://definitions (Asset/threat/control definitions) is accessible without any authentication or authorization check. Response size: 7384 chars." + }, + "properties": { + "confidence": 80, + "cveIds": [], + "cweIds": [ + "CWE-200" + ], + "severity": "low" + }, + "ruleId": "guardlink-data-exposure-mcp" + }, + { + "level": "note", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP resource guardlink://unmitigated (Unmitigated exposure list) is accessible without any authentication or authorization check. Response size: 2361 chars." + }, + "properties": { + "confidence": 80, + "cveIds": [], + "cweIds": [ + "CWE-200" + ], + "severity": "low" + }, + "ruleId": "guardlink-data-exposure-mcp" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP server accepted root='/etc' and returned data from outside the project directory. An MCP client can read arbitrary directory contents through the parse tool." + }, + "properties": { + "confidence": 85, + "cveIds": [], + "cweIds": [ + "CWE-22" + ], + "severity": "high" + }, + "ruleId": "guardlink-path-traversal-mcp" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP server accepted root='/etc' in guardlink_status and returned status data for files outside the project." + }, + "properties": { + "confidence": 85, + "cveIds": [], + "cweIds": [ + "CWE-22" + ], + "severity": "high" + }, + "ruleId": "guardlink-path-traversal-mcp" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The MCP guardlink_report tool wrote a file to /var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/guardlink-cxg-mcp-266fkcf_/stolen-report.md outside the project root by manipulating the output parameter." + }, + "properties": { + "confidence": 85, + "cveIds": [], + "cweIds": [ + "CWE-22" + ], + "severity": "high" + }, + "ruleId": "guardlink-path-traversal-mcp" + }, + { + "level": "error", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/path/to/guardlink-sample" + } + } + } + ], + "message": { + "text": "The 'guardlink clear' command follows symlinks and would modify files outside the project root. External file '/var/folders/zn/h5q5_5155fzcq32ddvgz2v0r0000gn/T/cxg-arb-write-7lyia0j6/external.ts' was included in the clear operation via a symlink." + }, + "properties": { + "confidence": 80, + "cveIds": [], + "cweIds": [ + "CWE-73" + ], + "severity": "high" + }, + "ruleId": "guardlink-parser-arbitrary-write" + } + ], + "tool": { + "driver": { + "informationUri": "https://cert-x-gen.io", + "name": "CERT-X-GEN", + "version": "1.1.0" + } + } + } + ], + "version": "2.1.0" +} \ No newline at end of file diff --git a/threat-dashboard.html b/docs/examples/threat-dashboard.html similarity index 54% rename from threat-dashboard.html rename to docs/examples/threat-dashboard.html index b81e728..faa3127 100644 --- a/threat-dashboard.html +++ b/docs/examples/threat-dashboard.html @@ -3,256 +3,1310 @@ -GuardLink — unknown Threat Model +GuardLink — guardlink Threat Model @@ -264,20 +1318,37 @@
-

unknown

+

guardlink

Threat Model
-
Assets 16
-
Open 15
-
Controls 12
-
Coverage 0%
+
+
Assets 16
+
Open 16
+
Controls 12
+
Coverage 0%
+
+
+ +
+ +
+ Filtered to feature: + + + +
+
@@ -285,6 +1356,7 @@

unknown