guardlink 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -7
- package/README.md +53 -5
- package/dist/agents/config.d.ts +7 -0
- package/dist/agents/config.d.ts.map +1 -1
- package/dist/agents/config.js.map +1 -1
- package/dist/agents/index.d.ts +9 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +36 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/launcher.d.ts.map +1 -1
- package/dist/agents/launcher.js +5 -0
- package/dist/agents/launcher.js.map +1 -1
- package/dist/agents/prompts.d.ts +16 -1
- package/dist/agents/prompts.d.ts.map +1 -1
- package/dist/agents/prompts.js +511 -16
- package/dist/agents/prompts.js.map +1 -1
- package/dist/analyze/format.d.ts +72 -0
- package/dist/analyze/format.d.ts.map +1 -0
- package/dist/analyze/format.js +176 -0
- package/dist/analyze/format.js.map +1 -0
- package/dist/analyze/index.d.ts +76 -0
- package/dist/analyze/index.d.ts.map +1 -1
- package/dist/analyze/index.js +165 -2
- package/dist/analyze/index.js.map +1 -1
- package/dist/analyze/prompts.d.ts +3 -2
- package/dist/analyze/prompts.d.ts.map +1 -1
- package/dist/analyze/prompts.js +17 -3
- package/dist/analyze/prompts.js.map +1 -1
- package/dist/analyzer/sarif.d.ts +3 -2
- package/dist/analyzer/sarif.d.ts.map +1 -1
- package/dist/analyzer/sarif.js +29 -3
- package/dist/analyzer/sarif.js.map +1 -1
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +408 -37
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/data.d.ts +11 -0
- package/dist/dashboard/data.d.ts.map +1 -1
- package/dist/dashboard/data.js +12 -0
- package/dist/dashboard/data.js.map +1 -1
- package/dist/dashboard/diagrams.d.ts +81 -12
- package/dist/dashboard/diagrams.d.ts.map +1 -1
- package/dist/dashboard/diagrams.js +750 -362
- package/dist/dashboard/diagrams.js.map +1 -1
- package/dist/dashboard/generate.d.ts +5 -2
- package/dist/dashboard/generate.d.ts.map +1 -1
- package/dist/dashboard/generate.js +2516 -244
- package/dist/dashboard/generate.js.map +1 -1
- package/dist/diff/engine.d.ts +2 -1
- package/dist/diff/engine.d.ts.map +1 -1
- package/dist/diff/engine.js +3 -2
- package/dist/diff/engine.js.map +1 -1
- package/dist/diff/git.js +3 -3
- package/dist/diff/git.js.map +1 -1
- package/dist/init/index.d.ts +7 -0
- package/dist/init/index.d.ts.map +1 -1
- package/dist/init/index.js +82 -27
- package/dist/init/index.js.map +1 -1
- package/dist/init/migrate.d.ts +39 -0
- package/dist/init/migrate.d.ts.map +1 -0
- package/dist/init/migrate.js +45 -0
- package/dist/init/migrate.js.map +1 -0
- package/dist/init/templates.d.ts +8 -0
- package/dist/init/templates.d.ts.map +1 -1
- package/dist/init/templates.js +68 -6
- package/dist/init/templates.js.map +1 -1
- package/dist/mcp/lookup.d.ts +1 -0
- package/dist/mcp/lookup.d.ts.map +1 -1
- package/dist/mcp/lookup.js +138 -10
- package/dist/mcp/lookup.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +32 -15
- package/dist/mcp/server.js.map +1 -1
- package/dist/parser/clear.d.ts +2 -1
- package/dist/parser/clear.d.ts.map +1 -1
- package/dist/parser/clear.js +19 -29
- package/dist/parser/clear.js.map +1 -1
- package/dist/parser/comment-strip.d.ts +5 -0
- package/dist/parser/comment-strip.d.ts.map +1 -1
- package/dist/parser/comment-strip.js +8 -0
- package/dist/parser/comment-strip.js.map +1 -1
- package/dist/parser/feature-filter.d.ts +42 -0
- package/dist/parser/feature-filter.d.ts.map +1 -0
- package/dist/parser/feature-filter.js +109 -0
- package/dist/parser/feature-filter.js.map +1 -0
- package/dist/parser/format.d.ts +24 -0
- package/dist/parser/format.d.ts.map +1 -0
- package/dist/parser/format.js +29 -0
- package/dist/parser/format.js.map +1 -0
- package/dist/parser/index.d.ts +2 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +1 -0
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parse-file.d.ts +1 -0
- package/dist/parser/parse-file.d.ts.map +1 -1
- package/dist/parser/parse-file.js +34 -9
- package/dist/parser/parse-file.js.map +1 -1
- package/dist/parser/parse-line.d.ts +9 -0
- package/dist/parser/parse-line.d.ts.map +1 -1
- package/dist/parser/parse-line.js +100 -26
- package/dist/parser/parse-line.js.map +1 -1
- package/dist/parser/parse-project.d.ts +1 -0
- package/dist/parser/parse-project.d.ts.map +1 -1
- package/dist/parser/parse-project.js +36 -2
- package/dist/parser/parse-project.js.map +1 -1
- package/dist/parser/validate.d.ts +3 -0
- package/dist/parser/validate.d.ts.map +1 -1
- package/dist/parser/validate.js +7 -0
- package/dist/parser/validate.js.map +1 -1
- package/dist/report/index.d.ts +1 -0
- package/dist/report/index.d.ts.map +1 -1
- package/dist/report/index.js +1 -0
- package/dist/report/index.js.map +1 -1
- package/dist/report/report.d.ts.map +1 -1
- package/dist/report/report.js +924 -24
- package/dist/report/report.js.map +1 -1
- package/dist/report/sequence.d.ts +11 -0
- package/dist/report/sequence.d.ts.map +1 -0
- package/dist/report/sequence.js +140 -0
- package/dist/report/sequence.js.map +1 -0
- package/dist/review/index.d.ts +3 -1
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +77 -35
- package/dist/review/index.js.map +1 -1
- package/dist/tui/commands.d.ts +1 -0
- package/dist/tui/commands.d.ts.map +1 -1
- package/dist/tui/commands.js +98 -12
- package/dist/tui/commands.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +7 -2
- package/dist/tui/index.js.map +1 -1
- package/dist/types/index.d.ts +59 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/workspace/merge.d.ts.map +1 -1
- package/dist/workspace/merge.js +6 -2
- package/dist/workspace/merge.js.map +1 -1
- package/package.json +1 -1
package/dist/agents/prompts.js
CHANGED
|
@@ -7,20 +7,66 @@
|
|
|
7
7
|
* @audit #agent-launcher -- "Prompt injection mitigated by agent's own safety measures; GuardLink prompt is read-only context"
|
|
8
8
|
* @exposes #agent-launcher to #path-traversal [medium] cwe:CWE-22 -- "Reads reference docs from root-relative paths"
|
|
9
9
|
* @mitigates #agent-launcher against #path-traversal using #path-validation -- "resolve() with root constrains file access"
|
|
10
|
+
* @exposes #agent-launcher to #config-tamper [medium] cwe:CWE-15 -- "Translate prompt may read CXG reference paths from environment overrides"
|
|
11
|
+
* @audit #agent-launcher -- "Environment override paths are optional convenience; verify trusted local paths in CI"
|
|
10
12
|
* @flows UserPrompt -> #agent-launcher via buildAnnotatePrompt -- "User instruction input"
|
|
13
|
+
* @flows UserPrompt -> #agent-launcher via buildTranslatePrompt -- "Template translation instruction input"
|
|
14
|
+
* @flows UserPrompt -> #agent-launcher via buildAskPrompt -- "Threat model question input"
|
|
11
15
|
* @flows ThreatModel -> #agent-launcher via model -- "Model context injection"
|
|
12
16
|
* @flows #agent-launcher -> AgentPrompt via return -- "Assembled prompt output"
|
|
13
17
|
* @handles internal on #agent-launcher -- "Serializes threat model IDs and flows into prompt"
|
|
14
18
|
*/
|
|
15
19
|
import { existsSync, readFileSync } from 'node:fs';
|
|
16
20
|
import { resolve } from 'node:path';
|
|
21
|
+
import { homedir } from 'node:os';
|
|
22
|
+
// CXG canonical install layout — matches what 'cargo install cert-x-gen'
|
|
23
|
+
// produces and what 'cxg template fetch' writes. The 'official' subdirectory
|
|
24
|
+
// is CXG's name for the Bugb-Technologies/cert-x-gen-templates remote;
|
|
25
|
+
// see PathResolver::user_template_dir() in cert-x-gen/src/template/paths.rs.
|
|
26
|
+
// Override with GUARDLINK_CXG_ROOT for forks or non-standard layouts.
|
|
27
|
+
const DEFAULT_CXG_ROOT = resolve(homedir(), '.cert-x-gen', 'templates', 'official');
|
|
28
|
+
const DEFAULT_CXG_SKELETON_DIR = resolve(DEFAULT_CXG_ROOT, 'templates', 'skeleton');
|
|
29
|
+
function readIfExists(path, maxChars = 5000) {
|
|
30
|
+
if (!existsSync(path))
|
|
31
|
+
return '';
|
|
32
|
+
try {
|
|
33
|
+
return readFileSync(path, 'utf-8').slice(0, maxChars);
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function annotationModeLabel(mode) {
|
|
40
|
+
return mode === 'external' ? 'externalized .gal files' : 'inline source comments';
|
|
41
|
+
}
|
|
42
|
+
function annotationModeInstructions(mode) {
|
|
43
|
+
if (mode === 'external') {
|
|
44
|
+
return `## Annotation Placement Mode
|
|
45
|
+
You MUST write annotations into associated standalone \`.gal\` files, not inline in the source code.
|
|
46
|
+
|
|
47
|
+
- Keep definitions in \`.guardlink/definitions.*\`
|
|
48
|
+
- For each annotated source file, create or update an associated file under \`.guardlink/annotations/\`
|
|
49
|
+
- Mirror the source path in the annotation file path (example: \`src/auth/login.ts\` -> \`.guardlink/annotations/src/auth/login.ts.gal\`)
|
|
50
|
+
- Group annotations under \`@source file:<path> line:<n> [symbol:<name>]\` so each block points at the real code location
|
|
51
|
+
- In \`.gal\` files, write raw GAL lines without \`//\` or \`#\` prefixes
|
|
52
|
+
- Do NOT modify source files just to add comments when this mode is selected
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
return `## Annotation Placement Mode
|
|
56
|
+
You MUST write annotations inline in the source code comments.
|
|
57
|
+
|
|
58
|
+
- Place annotations in the file doc-block or directly above the security-relevant code
|
|
59
|
+
- Use the host language comment syntax (\`//\`, \`#\`, \`--\`, etc.)
|
|
60
|
+
- Do NOT externalize annotations into \`.gal\` files when this mode is selected
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
17
63
|
/**
|
|
18
64
|
* Build a prompt for annotation agents.
|
|
19
65
|
*
|
|
20
66
|
* Includes the GuardLink reference doc, current model summary with flows and exposures,
|
|
21
67
|
* flow-first threat modeling methodology, and precise GAL syntax rules.
|
|
22
68
|
*/
|
|
23
|
-
export function buildAnnotatePrompt(userPrompt, root, model) {
|
|
69
|
+
export function buildAnnotatePrompt(userPrompt, root, model, annotationMode = 'inline') {
|
|
24
70
|
// Read the reference doc if available
|
|
25
71
|
let refDoc = '';
|
|
26
72
|
const refPath = resolve(root, '.guardlink', 'GUARDLINK_REFERENCE.md');
|
|
@@ -42,6 +88,7 @@ export function buildAnnotatePrompt(userPrompt, root, model) {
|
|
|
42
88
|
const parts = [
|
|
43
89
|
`${model.annotations_parsed} annotations`,
|
|
44
90
|
`${model.exposures.length} exposures`,
|
|
91
|
+
...((model.confirmed || []).length > 0 ? [`${model.confirmed.length} confirmed exploitable`] : []),
|
|
45
92
|
`${model.assets.length} assets`,
|
|
46
93
|
`${model.threats.length} threats`,
|
|
47
94
|
`${model.controls.length} controls`,
|
|
@@ -85,6 +132,7 @@ export function buildAnnotatePrompt(userPrompt, root, model) {
|
|
|
85
132
|
}
|
|
86
133
|
return `You are an expert security engineer performing threat modeling as code.
|
|
87
134
|
Your job is to read this codebase deeply, understand how code flows between components, and annotate it with GuardLink (GAL) security annotations that accurately represent the security posture.
|
|
135
|
+
This run MUST produce annotations as ${annotationModeLabel(annotationMode)}.
|
|
88
136
|
|
|
89
137
|
This is NOT a vulnerability scanner. You are building a living threat model embedded in the code itself.
|
|
90
138
|
Annotations capture what COULD go wrong, what controls exist, and how data moves — not just confirmed bugs.
|
|
@@ -95,6 +143,8 @@ ${modelSummary}${existingIds}${existingFlows}${existingExposures}
|
|
|
95
143
|
## Your Task
|
|
96
144
|
${userPrompt}
|
|
97
145
|
|
|
146
|
+
${annotationModeInstructions(annotationMode)}
|
|
147
|
+
|
|
98
148
|
## HOW TO THINK — Flow-First Threat Modeling
|
|
99
149
|
|
|
100
150
|
Before writing ANY annotation, you MUST understand the code deeply:
|
|
@@ -197,19 +247,35 @@ Place @boundary annotations where trust level changes between two components:
|
|
|
197
247
|
\`\`\`
|
|
198
248
|
|
|
199
249
|
### Where to Place Annotations
|
|
200
|
-
|
|
250
|
+
${annotationMode === 'external'
|
|
251
|
+
? 'Annotations go in associated `.gal` files, grouped by `@source` blocks that point at the real code location:'
|
|
252
|
+
: "Annotations go in the file's top doc-block comment OR directly above the security-relevant code:"}
|
|
201
253
|
|
|
202
254
|
\`\`\`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
255
|
+
${annotationMode === 'external'
|
|
256
|
+
? [
|
|
257
|
+
'@shield:begin -- "Externalized annotation examples, excluded from parsing"',
|
|
258
|
+
'@source file:src/auth/login.ts line:42 symbol:authenticate',
|
|
259
|
+
'@exposes #auth-api to #sqli [P1] cwe:CWE-89 -- "User-supplied email reaches query builder"',
|
|
260
|
+
'@mitigates #auth-api against #sqli using #input-validation -- "Zod schema validates email before query"',
|
|
261
|
+
'@comment -- "Externalized annotations for src/auth/login.ts"',
|
|
262
|
+
'',
|
|
263
|
+
'@source file:src/auth/session.ts line:88 symbol:issueToken',
|
|
264
|
+
'@handles secrets on #auth-api -- "Issues session token"',
|
|
265
|
+
'@shield:end',
|
|
266
|
+
].join('\n')
|
|
267
|
+
: [
|
|
268
|
+
'// @shield:begin -- "Placement examples, excluded from parsing"',
|
|
269
|
+
'//',
|
|
270
|
+
'// FILE-LEVEL (top doc-block) — for module-wide security properties:',
|
|
271
|
+
'// Place @exposes, @mitigates, @flows, @handles, @boundary that describe the module as a whole',
|
|
272
|
+
'//',
|
|
273
|
+
'// INLINE (above specific functions/methods) — for function-specific concerns:',
|
|
274
|
+
'// Place @exposes, @mitigates above the exact function where the risk or control lives',
|
|
275
|
+
'// Place @comment above tricky security-relevant code to explain intent',
|
|
276
|
+
'//',
|
|
277
|
+
'// @shield:end',
|
|
278
|
+
].join('\n')}
|
|
213
279
|
\`\`\`
|
|
214
280
|
|
|
215
281
|
### Severity — Be Honest, Not Alarmist
|
|
@@ -251,6 +317,16 @@ Example — what to do when no mitigation exists:
|
|
|
251
317
|
|
|
252
318
|
Leaving exposures unmitigated is HONEST. The dashboard and reports will surface them as open risks for humans to triage.
|
|
253
319
|
|
|
320
|
+
### Pentest-Confirmable vs Governance-Only Gaps
|
|
321
|
+
When documenting threats, distinguish between:
|
|
322
|
+
1. **Pentest-confirmable findings**: testable with concrete I/O behavior (e.g., injection, auth bypass, IDOR, exposed service, unsafe deserialization). Document the risk with @exposes (hypothesis). After a pentest, CXG scan, or manual reproduction **proves** exploitability with evidence, add @confirmed #threat on Asset [severity] -- "evidence summary" — never use @confirmed for guesses or scanner noise without verification.
|
|
323
|
+
2. **Governance/design gaps**: important risks that are not directly testable as a penetration test template (e.g., missing ownership process, policy-only controls, broad architectural assumptions with no direct exploit path).
|
|
324
|
+
|
|
325
|
+
For governance/design gaps:
|
|
326
|
+
- Do NOT force a fake exploit-style exposure.
|
|
327
|
+
- Add @audit on the relevant asset with precise reasoning.
|
|
328
|
+
- Add @comment suggesting concrete controls or follow-up review tasks.
|
|
329
|
+
|
|
254
330
|
### @shield — DO NOT USE Unless Explicitly Asked
|
|
255
331
|
@shield and @shield:begin/@shield:end block AI coding assistants from reading the annotated code.
|
|
256
332
|
This means any shielded code becomes invisible to AI tools — they cannot analyze, refactor, or annotate it.
|
|
@@ -259,7 +335,7 @@ Adding @shield on your own initiative would actively harm the threat model by cr
|
|
|
259
335
|
|
|
260
336
|
## PRECISE GAL Syntax
|
|
261
337
|
|
|
262
|
-
Definitions go in .guardlink/definitions.{ts,js,py,rs}.
|
|
338
|
+
Definitions go in .guardlink/definitions.{ts,js,py,rs}. Relationship annotations can live in source comments or standalone .gal files.
|
|
263
339
|
|
|
264
340
|
### Definitions (in .guardlink/definitions file)
|
|
265
341
|
\`\`\`
|
|
@@ -274,6 +350,7 @@ Definitions go in .guardlink/definitions.{ts,js,py,rs}. Source files use only re
|
|
|
274
350
|
\`\`\`
|
|
275
351
|
// @shield:begin -- "Relationship syntax examples, excluded from parsing"
|
|
276
352
|
// @exposes #auth to #sqli [P0] cwe:CWE-89 owasp:A03:2021 -- "User input concatenated into query"
|
|
353
|
+
// @confirmed #sqli on #auth [critical] cwe:CWE-89 -- "Pentest 2026-04: time-based blind SQLi on /login confirmed"
|
|
277
354
|
// @mitigates #auth against #sqli using #prepared-stmts -- "Uses parameterized queries via sqlx"
|
|
278
355
|
// @audit #auth -- "Timing attack risk — needs human review to decide if bcrypt constant-time comparison is sufficient"
|
|
279
356
|
// @transfers #ddos from #api to #cdn -- "Cloudflare handles L7 DDoS mitigation"
|
|
@@ -284,10 +361,19 @@ Definitions go in .guardlink/definitions.{ts,js,py,rs}. Source files use only re
|
|
|
284
361
|
// @audit #auth -- "Session token rotation logic needs cryptographic review"
|
|
285
362
|
// @assumes #auth -- "Upstream API gateway has already validated TLS and rate-limited requests"
|
|
286
363
|
// @owns security-team for #auth -- "Security team reviews all auth PRs"
|
|
364
|
+
// @feature "SSO Login" -- "Single sign-on authentication flow"
|
|
287
365
|
// @comment -- "Password hashing uses bcrypt with cost factor 12, migration from SHA256 completed in v2.1"
|
|
288
366
|
// @shield:end
|
|
289
367
|
\`\`\`
|
|
290
368
|
|
|
369
|
+
### Relationships (in standalone .gal files)
|
|
370
|
+
\`\`\`
|
|
371
|
+
@source file:src/auth/login.ts line:42 symbol:authenticate
|
|
372
|
+
@exposes #auth to #sqli [P0] cwe:CWE-89 owasp:A03:2021 -- "User input concatenated into query"
|
|
373
|
+
@mitigates #auth against #sqli using #prepared-stmts -- "Uses parameterized queries via sqlx"
|
|
374
|
+
@audit #auth -- "Timing attack risk — needs human review"
|
|
375
|
+
\`\`\`
|
|
376
|
+
|
|
291
377
|
## CRITICAL SYNTAX RULES (violations cause parse errors)
|
|
292
378
|
|
|
293
379
|
1. **@boundary requires TWO assets**: \`@boundary between #A and #B\` or \`@boundary #A | #B\`.
|
|
@@ -304,6 +390,7 @@ Definitions go in .guardlink/definitions.{ts,js,py,rs}. Source files use only re
|
|
|
304
390
|
|
|
305
391
|
4. **Severity in square brackets**: \`[P0]\` \`[P1]\` \`[P2]\` \`[P3]\` or \`[critical]\` \`[high]\` \`[medium]\` \`[low]\`.
|
|
306
392
|
Goes AFTER the threat ref in @exposes: \`@exposes #app to #sqli [P0] cwe:CWE-89\`
|
|
393
|
+
On @confirmed, severity is optional but recommended — it reflects **verified** impact: \`@confirmed #sqli on #app [critical] -- "evidence"\`
|
|
307
394
|
|
|
308
395
|
5. **Descriptions in double quotes after --**: \`-- "description text here"\`
|
|
309
396
|
WRONG: \`@comment "just a note"\` or \`@comment -- note without quotes\`
|
|
@@ -322,6 +409,7 @@ Definitions go in .guardlink/definitions.{ts,js,py,rs}. Source files use only re
|
|
|
322
409
|
A bare \`@comment\` without description is valid but useless. Always include context.
|
|
323
410
|
|
|
324
411
|
10. **One annotation per comment line.** Do NOT put two @verbs on the same line.
|
|
412
|
+
11. **In external mode, use \`@source\` before each block** so the annotations point at the intended file and line.
|
|
325
413
|
|
|
326
414
|
## Workflow
|
|
327
415
|
|
|
@@ -339,11 +427,418 @@ Definitions go in .guardlink/definitions.{ts,js,py,rs}. Source files use only re
|
|
|
339
427
|
Think: "what's the risk, what's the defense, how does data flow here, and what should the next developer know?"
|
|
340
428
|
NEVER write @accepts — that is a human-only governance decision. Use @audit to flag unmitigated risks for review.
|
|
341
429
|
|
|
342
|
-
5. **Use the
|
|
430
|
+
5. **Use the selected annotation mode consistently.** Inline mode writes source comments; external mode writes associated \`.gal\` files with \`@source\` blocks.
|
|
431
|
+
|
|
432
|
+
6. **Generate project description.** If \`.guardlink/prompt.md\` exists and contains only the skeleton template
|
|
433
|
+
(HTML comments / placeholder headings with no real content), fill it in based on what you learned while
|
|
434
|
+
reading the codebase. Write a security-focused project overview covering:
|
|
435
|
+
- What the application does and who its users are
|
|
436
|
+
- Key components and services
|
|
437
|
+
- Trust boundaries (where trust changes between components)
|
|
438
|
+
- Data sensitivity (PII, credentials, financial data, etc.)
|
|
439
|
+
- Deployment context (cloud, containers, CI/CD, etc.)
|
|
440
|
+
This file feeds into \`guardlink report\` as the Application Overview section.
|
|
441
|
+
**Do NOT overwrite user-written content** — only fill in the template placeholders.
|
|
343
442
|
|
|
344
|
-
|
|
443
|
+
7. **Run validation** via guardlink_validate (MCP) or \`guardlink validate\` to check for errors.
|
|
345
444
|
|
|
346
|
-
|
|
445
|
+
8. **Fix any validation errors** before finishing — especially dangling refs and malformed syntax.
|
|
446
|
+
`;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Build a prompt for translating GuardLink threat model findings into
|
|
450
|
+
* CERT-X-GEN (CXG) pentest templates.
|
|
451
|
+
*/
|
|
452
|
+
export function buildTranslatePrompt(userPrompt, root, model) {
|
|
453
|
+
const cxgRoot = process.env.GUARDLINK_CXG_ROOT || DEFAULT_CXG_ROOT;
|
|
454
|
+
const skeletonDir = process.env.GUARDLINK_CXG_SKELETON_DIR || DEFAULT_CXG_SKELETON_DIR;
|
|
455
|
+
const templateGuide = readIfExists(resolve(cxgRoot, 'docs', 'TEMPLATE_GUIDE.md'), 4000);
|
|
456
|
+
// prompt.rs only exists when GUARDLINK_CXG_ROOT points at the CXG source
|
|
457
|
+
// repo (developer use). For end users on the installed layout the path
|
|
458
|
+
// resolves under ~/.cert-x-gen/templates/official/src/ai/prompt.rs which
|
|
459
|
+
// does not exist; readIfExists() returns '' gracefully and the prompt
|
|
460
|
+
// still works — they just get a slightly leaner template-authoring guide.
|
|
461
|
+
const promptEngine = readIfExists(resolve(cxgRoot, 'src', 'ai', 'prompt.rs'), 4000);
|
|
462
|
+
const yamlSkeleton = readIfExists(resolve(skeletonDir, 'yaml-template-skeleton.yaml'), 5000);
|
|
463
|
+
const pythonSkeleton = readIfExists(resolve(skeletonDir, 'python-template-skeleton.py'), 3000);
|
|
464
|
+
let modelSummary = 'No threat model parsed yet.';
|
|
465
|
+
let candidateExposures = '';
|
|
466
|
+
if (model) {
|
|
467
|
+
const unmitigated = model.exposures.filter((e) => !model.mitigations.some((m) => m.asset === e.asset && m.threat === e.threat));
|
|
468
|
+
modelSummary = `Current model: ${model.annotations_parsed} annotations, ${model.exposures.length} exposures, ${(model.confirmed || []).length} confirmed, ${unmitigated.length} unmitigated exposures, ${model.assets.length} assets, ${model.threats.length} threats.`;
|
|
469
|
+
if (unmitigated.length > 0) {
|
|
470
|
+
const lines = unmitigated.slice(0, 40).map((e) => `- ${e.asset} -> ${e.threat} [${e.severity || 'unrated'}] (${e.location.file}:${e.location.line})`);
|
|
471
|
+
candidateExposures = `\n\nUnmitigated exposure candidates:\n${lines.join('\n')}`;
|
|
472
|
+
if (unmitigated.length > 40) {
|
|
473
|
+
candidateExposures += `\n- ... and ${unmitigated.length - 40} more`;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const instruction = userPrompt.trim()
|
|
478
|
+
? userPrompt.trim()
|
|
479
|
+
: 'Generate CXG pentest templates for all pentest-confirmable high/critical threats first, then medium.';
|
|
480
|
+
return `You are a senior offensive security engineer translating GuardLink threat-model findings into CERT-X-GEN (CXG) templates.
|
|
481
|
+
|
|
482
|
+
## Mission
|
|
483
|
+
Convert pentest-confirmable threats into runnable CXG templates. Do NOT execute templates. Only author template files.
|
|
484
|
+
|
|
485
|
+
## Current Threat Model
|
|
486
|
+
${modelSummary}${candidateExposures}
|
|
487
|
+
|
|
488
|
+
## User Request
|
|
489
|
+
${instruction}
|
|
490
|
+
|
|
491
|
+
## Required CXG CLI Discovery (Do This First)
|
|
492
|
+
Before generating final user guidance, discover the actual CLI usage on this machine:
|
|
493
|
+
1. Try: \`cxg --help\`
|
|
494
|
+
2. Try: \`cxg scan --help\`
|
|
495
|
+
3. Try: \`cxg template --help\`
|
|
496
|
+
4. If \`cxg\` is not in PATH, try local binary from source checkout (if present):
|
|
497
|
+
- \`cxg --help\`
|
|
498
|
+
- \`cxg scan --help\`
|
|
499
|
+
- \`cxg template --help\`
|
|
500
|
+
5. Base user instructions on the commands that actually work. If none work, clearly state the blocker and provide install/build steps first.
|
|
501
|
+
|
|
502
|
+
## Required Decision Rule (Critical)
|
|
503
|
+
For every candidate threat/exposure:
|
|
504
|
+
1. Decide if it is **pentest-confirmable** — meaning it can be validated via:
|
|
505
|
+
- Network request/response behavior (HTTP, TCP, etc.)
|
|
506
|
+
- Local CLI invocation with crafted inputs (command injection, path traversal, etc.)
|
|
507
|
+
- File system operations (symlink attacks, arbitrary writes, config tampering)
|
|
508
|
+
- MCP/stdio protocol interactions (JSON-RPC tool calls with malicious payloads)
|
|
509
|
+
- Process spawning behavior (canary file creation, shell metacharacter interpretation)
|
|
510
|
+
2. If yes: create one or more CXG templates. For local CLI/codebase threats, templates should use \`subprocess.run()\` or \`subprocess.Popen()\` with \`cwd=target\` to invoke the tool under test.
|
|
511
|
+
3. If no (pure governance/process/design gap): do NOT create a template. Instead document it as audit-only guidance:
|
|
512
|
+
- Include suggested GuardLink @audit text and @comment text for the relevant asset/file.
|
|
513
|
+
- Explain briefly why no pentest template is appropriate.
|
|
514
|
+
|
|
515
|
+
## Output and File Operations
|
|
516
|
+
1. Create templates under: \`.guardlink/cxg-templates/\`
|
|
517
|
+
2. Use meaningful filenames like:
|
|
518
|
+
- \`.guardlink/cxg-templates/<threat-id-or-name>.yaml\`
|
|
519
|
+
- or language variants \`.py\`, \`.js\`, \`.go\`, etc. if needed.
|
|
520
|
+
3. Write an index file at \`.guardlink/cxg-templates/README.md\` with:
|
|
521
|
+
- generated templates list
|
|
522
|
+
- mapping: GuardLink threat/exposure -> template file(s)
|
|
523
|
+
- "audit-only / no-template" items with suggested @audit annotations
|
|
524
|
+
4. CXG scan output goes to: \`.guardlink/pentest-findings/\` (this is where \`guardlink dashboard\` and \`guardlink threat-report\` read pentest results from). Always tell users to output to this path.
|
|
525
|
+
5. Do NOT run CXG CLI or execute generated templates.
|
|
526
|
+
6. Keep checks non-destructive.
|
|
527
|
+
7. You MAY run \`cxg --help\` and other help/listing commands only for usage discovery. Do not run active scans unless user explicitly asks.
|
|
528
|
+
|
|
529
|
+
## CXG Format Contract (from source)
|
|
530
|
+
Use the project skeleton contract and examples; mirror field names and structure exactly.
|
|
531
|
+
|
|
532
|
+
${templateGuide ? `### TEMPLATE_GUIDE excerpt\n${templateGuide}\n` : ''}
|
|
533
|
+
${promptEngine ? `### prompt.rs excerpt\n${promptEngine}\n` : ''}
|
|
534
|
+
${yamlSkeleton ? `### YAML skeleton excerpt\n${yamlSkeleton}\n` : ''}
|
|
535
|
+
${pythonSkeleton ? `### Python skeleton excerpt\n${pythonSkeleton}\n` : ''}
|
|
536
|
+
|
|
537
|
+
## Quality Bar
|
|
538
|
+
- Each template must include clear metadata: id/name/author/severity/description/tags/references.
|
|
539
|
+
- Detection logic must align to the threat and include concrete matchers/assertions.
|
|
540
|
+
- Prefer YAML templates for declarative checks; use code templates where procedural logic is required.
|
|
541
|
+
- Avoid placeholder TODO logic.
|
|
542
|
+
- Keep template logic scoped to the specific threat confirmation.
|
|
543
|
+
|
|
544
|
+
## CXG Engine Contract (Critical — templates MUST follow this)
|
|
545
|
+
When CXG runs a Python template, it does NOT pass the target as a CLI argument.
|
|
546
|
+
Instead, it sets environment variables and expects JSON on stdout.
|
|
547
|
+
|
|
548
|
+
### Target resolution (in main / entry point):
|
|
549
|
+
\`\`\`python
|
|
550
|
+
target = os.environ.get("CERT_X_GEN_PROJECT_ROOT") or args.target or os.environ.get("CERT_X_GEN_TARGET_HOST")
|
|
551
|
+
\`\`\`
|
|
552
|
+
- \`CERT_X_GEN_PROJECT_ROOT\`: set for local codebase/CLI targets (absolute path).
|
|
553
|
+
- \`CERT_X_GEN_TARGET_HOST\`: set for network targets (hostname/IP).
|
|
554
|
+
- The positional \`target\` arg MUST use \`nargs="?"\` (optional) since CXG engine passes no argv.
|
|
555
|
+
|
|
556
|
+
### Output contract:
|
|
557
|
+
- When \`CERT_X_GEN_MODE == "engine"\` (always true under CXG), print ONLY a JSON array to stdout.
|
|
558
|
+
- Output \`[]\` (empty array) when no findings — never print plain text in engine mode.
|
|
559
|
+
- Use: \`print(json.dumps(findings, indent=2))\`
|
|
560
|
+
|
|
561
|
+
### Environment variables available:
|
|
562
|
+
| Variable | Value |
|
|
563
|
+
|----------|-------|
|
|
564
|
+
| \`CERT_X_GEN_MODE\` | Always \`"engine"\` |
|
|
565
|
+
| \`CERT_X_GEN_TARGET_HOST\` | Target address (path for local, hostname for network) |
|
|
566
|
+
| \`CERT_X_GEN_TARGET_TYPE\` | \`"local"\` or \`"network"\` |
|
|
567
|
+
| \`CERT_X_GEN_PROJECT_ROOT\` | Absolute path (local targets only) |
|
|
568
|
+
| \`CERT_X_GEN_TARGET_PORT\` | Port number (network targets, default 80) |
|
|
569
|
+
|
|
570
|
+
### Running templates with CXG local scope:
|
|
571
|
+
\`\`\`bash
|
|
572
|
+
cxg scan --scope local://. --template-dir .guardlink/cxg-templates/ --output .guardlink/pentest-findings/guardlink-pentest --output-format json,sarif,html
|
|
573
|
+
\`\`\`
|
|
574
|
+
|
|
575
|
+
## CXG Evidence Contract (Critical — findings MUST include rich evidence)
|
|
576
|
+
CXG parses finding evidence using specific field names. If these fields are missing or empty,
|
|
577
|
+
the output report will show blank evidence — making findings impossible to verify.
|
|
578
|
+
|
|
579
|
+
### Required evidence structure in every finding dict:
|
|
580
|
+
\`\`\`python
|
|
581
|
+
"evidence": {
|
|
582
|
+
"request": "<string: what was sent — payload, RPC call, CLI args, env vars, etc.>",
|
|
583
|
+
"response": "<string: what came back — stdout, stderr, HTTP response, RPC response, etc.>",
|
|
584
|
+
"matched_patterns": ["<string>", ...], # list of STRINGS (not dicts) — e.g. CWE IDs, indicators found, regex matches
|
|
585
|
+
"data": { # arbitrary key-value map for all raw evidence details
|
|
586
|
+
"key1": "<string value>",
|
|
587
|
+
"key2": "<string value>",
|
|
588
|
+
...
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
\`\`\`
|
|
592
|
+
|
|
593
|
+
### Rules for populating evidence:
|
|
594
|
+
1. **\`request\`**: MUST contain the exact input that triggered the finding. Examples:
|
|
595
|
+
- For CLI injection: the full command with payload (e.g., \`npx guardlink annotate "; touch /tmp/canary"\`)
|
|
596
|
+
- For MCP tests: the JSON-RPC request body sent to the tool
|
|
597
|
+
- For path traversal: the malicious path used (e.g., \`../../etc/passwd\`)
|
|
598
|
+
- For config tamper: the environment variable name and injected value
|
|
599
|
+
|
|
600
|
+
2. **\`response\`**: MUST contain the raw output that proves the vulnerability. Examples:
|
|
601
|
+
- stdout/stderr excerpt from the command execution (up to 2000 chars)
|
|
602
|
+
- The MCP JSON-RPC response content
|
|
603
|
+
- File contents read from an unexpected location
|
|
604
|
+
- Error messages that reveal injection
|
|
605
|
+
|
|
606
|
+
3. **\`matched_patterns\`**: MUST be a list of **strings** (CXG drops non-strings). Include:
|
|
607
|
+
- Shell error indicators found (e.g., "sh: command not found")
|
|
608
|
+
- Sensitive data patterns matched (e.g., "absolute_paths: 5 found")
|
|
609
|
+
- CWE/OWASP identifiers relevant to the finding
|
|
610
|
+
- Canary strings that proved exploitation
|
|
611
|
+
|
|
612
|
+
4. **\`data\`**: Store ALL evidence key-value pairs here. All values must be strings
|
|
613
|
+
(use \`json.dumps()\` to serialize complex objects). This is the catch-all for:
|
|
614
|
+
- \`canary_created\`: "true"
|
|
615
|
+
- \`exit_code\`: "0"
|
|
616
|
+
- \`symlink_path\`: "/path/to/symlink"
|
|
617
|
+
- \`traversal_root\`: "/etc"
|
|
618
|
+
- \`env_var\`: "GUARDLINK_CXG_ROOT"
|
|
619
|
+
|
|
620
|
+
### Helper pattern for \`create_finding\`:
|
|
621
|
+
Always use a centralized helper that maps your raw evidence dict into the CXG structure:
|
|
622
|
+
|
|
623
|
+
\`\`\`python
|
|
624
|
+
def create_finding(self, title, description, evidence):
|
|
625
|
+
return {
|
|
626
|
+
"template_id": self.id,
|
|
627
|
+
"title": title,
|
|
628
|
+
"severity": self.severity,
|
|
629
|
+
"confidence": self.confidence,
|
|
630
|
+
"description": description,
|
|
631
|
+
"evidence": {
|
|
632
|
+
"request": evidence.get("request") or evidence.get("payload") or evidence.get("rpc_request") or
|
|
633
|
+
json.dumps({k: v for k, v in evidence.items()
|
|
634
|
+
if k not in ("response", "stdout_excerpt", "stderr_excerpt",
|
|
635
|
+
"output_excerpt", "response_snippet", "matched_patterns")}, default=str),
|
|
636
|
+
"response": evidence.get("response") or evidence.get("stdout_excerpt") or
|
|
637
|
+
evidence.get("stderr_excerpt") or evidence.get("output_excerpt") or
|
|
638
|
+
evidence.get("response_snippet") or evidence.get("content_snippet") or "",
|
|
639
|
+
"matched_patterns": [p if isinstance(p, str) else
|
|
640
|
+
(f"{p.get('type','')}: {p.get('count','?')}" if isinstance(p, dict) else str(p))
|
|
641
|
+
for p in (evidence.get("matched_patterns") or [])],
|
|
642
|
+
"data": {k: (v if isinstance(v, str) else json.dumps(v, default=str))
|
|
643
|
+
for k, v in evidence.items()},
|
|
644
|
+
},
|
|
645
|
+
"cwe": self.cwe,
|
|
646
|
+
"tags": self.tags,
|
|
647
|
+
"remediation": "...",
|
|
648
|
+
}
|
|
649
|
+
\`\`\`
|
|
650
|
+
|
|
651
|
+
### What to capture as evidence for each template type:
|
|
652
|
+
| Template type | request | response | matched_patterns |
|
|
653
|
+
|---|---|---|---|
|
|
654
|
+
| CLI injection | Full CLI command with payload | stdout + stderr (first 2000 chars) | Shell indicators, canary proof |
|
|
655
|
+
| MCP tool call | JSON-RPC request body | JSON-RPC response body | Sensitive data types found |
|
|
656
|
+
| Path traversal | Traversal path used | File/dir content from outside project | Path indicators (/etc, /tmp) |
|
|
657
|
+
| Config tamper | Env var name + injected value | Command output with canary | Canary string match |
|
|
658
|
+
| Prompt injection | Injected prompt text | LLM/agent output text | Injection markers found |
|
|
659
|
+
| Arbitrary write | Symlink/path payload | guardlink clear output showing external files | External paths listed |
|
|
660
|
+
|
|
661
|
+
### NEVER do this:
|
|
662
|
+
- Do NOT pass raw evidence dicts without the CXG structure — CXG will show empty evidence fields.
|
|
663
|
+
- Do NOT put dicts or lists in \`matched_patterns\` — CXG drops non-string entries silently.
|
|
664
|
+
- Do NOT skip evidence collection — a finding without evidence is unverifiable.
|
|
665
|
+
|
|
666
|
+
## Python Template Boilerplate (MUST use this structure)
|
|
667
|
+
Every Python template you create MUST follow this exact \`main()\` structure:
|
|
668
|
+
|
|
669
|
+
\`\`\`python
|
|
670
|
+
def main():
|
|
671
|
+
parser = argparse.ArgumentParser(description="...")
|
|
672
|
+
parser.add_argument("target", nargs="?", help="Project root or target host")
|
|
673
|
+
parser.add_argument("--port", type=int, default=0)
|
|
674
|
+
parser.add_argument("--json", action="store_true")
|
|
675
|
+
args = parser.parse_args()
|
|
676
|
+
|
|
677
|
+
template = CertXGenTemplate()
|
|
678
|
+
target = os.environ.get("CERT_X_GEN_PROJECT_ROOT") or args.target or os.environ.get("CERT_X_GEN_TARGET_HOST")
|
|
679
|
+
if not target:
|
|
680
|
+
parser.error("target is required (positional, CERT_X_GEN_PROJECT_ROOT, or CERT_X_GEN_TARGET_HOST)")
|
|
681
|
+
|
|
682
|
+
findings = template.execute(target, args.port)
|
|
683
|
+
if args.json or os.environ.get("CERT_X_GEN_MODE") == "engine":
|
|
684
|
+
print(json.dumps(findings, indent=2))
|
|
685
|
+
elif findings:
|
|
686
|
+
for f in findings:
|
|
687
|
+
print(f"[{f['severity'].upper()}] {f['title']}")
|
|
688
|
+
print(f" {f['description']}")
|
|
689
|
+
print()
|
|
690
|
+
else:
|
|
691
|
+
print("No findings detected.")
|
|
692
|
+
|
|
693
|
+
if __name__ == "__main__":
|
|
694
|
+
main()
|
|
695
|
+
\`\`\`
|
|
696
|
+
|
|
697
|
+
Key rules:
|
|
698
|
+
- \`target\` positional arg uses \`nargs="?"\` — CXG engine does NOT pass target as argv.
|
|
699
|
+
- Target resolution order: \`CERT_X_GEN_PROJECT_ROOT\` > \`args.target\` > \`CERT_X_GEN_TARGET_HOST\`.
|
|
700
|
+
- When \`CERT_X_GEN_MODE == "engine"\`, ALWAYS output JSON (even if \`--json\` is not set).
|
|
701
|
+
- Output \`[]\` (empty JSON array) when no findings — never plain text in engine mode.
|
|
702
|
+
- For local/CLI templates, use \`target\` as \`cwd\` in \`subprocess.run()\` / \`subprocess.Popen()\` calls.
|
|
703
|
+
|
|
704
|
+
## Final Response Format
|
|
705
|
+
After writing files, return:
|
|
706
|
+
1. A short "Generated templates" list with file paths.
|
|
707
|
+
2. A short "Audit-only (no template)" list with recommended GuardLink @audit/@comment text.
|
|
708
|
+
3. A "How to run these templates with CXG" section with these **exact steps**:
|
|
709
|
+
|
|
710
|
+
**Step 1 — Prerequisites:**
|
|
711
|
+
\`\`\`bash
|
|
712
|
+
cxg --version # Verify CXG is installed (expect v1.1.0+)
|
|
713
|
+
python3 --version # Python 3.8+ required for template execution
|
|
714
|
+
ls .guardlink/cxg-templates/*.py # Verify templates were created
|
|
715
|
+
\`\`\`
|
|
716
|
+
|
|
717
|
+
**Step 2 — Validate templates:**
|
|
718
|
+
\`\`\`bash
|
|
719
|
+
cxg template validate .guardlink/cxg-templates/ --recursive
|
|
720
|
+
\`\`\`
|
|
721
|
+
|
|
722
|
+
**Step 3 — Create output directory and run scan using local scope (for CLI/codebase targets):**
|
|
723
|
+
\`\`\`bash
|
|
724
|
+
mkdir -p .guardlink/pentest-findings
|
|
725
|
+
cxg scan \\
|
|
726
|
+
--scope local://. \\
|
|
727
|
+
--template-dir .guardlink/cxg-templates/ \\
|
|
728
|
+
--template-language python \\
|
|
729
|
+
--output .guardlink/pentest-findings/guardlink-pentest \\
|
|
730
|
+
--output-format json,sarif,html
|
|
731
|
+
\`\`\`
|
|
732
|
+
The \`local://.\` scope tells CXG this is a local codebase target. CXG will set
|
|
733
|
+
\`CERT_X_GEN_PROJECT_ROOT\` to the absolute path of the current directory and
|
|
734
|
+
\`CERT_X_GEN_TARGET_TYPE=local\`, so templates receive the correct project root.
|
|
735
|
+
|
|
736
|
+
Output is stored in \`.guardlink/pentest-findings/\` so that \`guardlink dashboard\`
|
|
737
|
+
and \`guardlink threat-report\` automatically pick up the results.
|
|
738
|
+
|
|
739
|
+
**Step 3b — Run scan using network scope (for HTTP/API targets):**
|
|
740
|
+
\`\`\`bash
|
|
741
|
+
cxg scan \\
|
|
742
|
+
--scope https://api.example.com \\
|
|
743
|
+
--template-dir .guardlink/cxg-templates/ \\
|
|
744
|
+
--output .guardlink/pentest-findings/guardlink-pentest \\
|
|
745
|
+
--output-format json,sarif,html
|
|
746
|
+
\`\`\`
|
|
747
|
+
|
|
748
|
+
**Step 4 — Run with verbose output for debugging:**
|
|
749
|
+
\`\`\`bash
|
|
750
|
+
cxg -vv scan \\
|
|
751
|
+
--scope local://. \\
|
|
752
|
+
--template-dir .guardlink/cxg-templates/ \\
|
|
753
|
+
--output .guardlink/pentest-findings/guardlink-pentest \\
|
|
754
|
+
--output-format json,sarif,html
|
|
755
|
+
\`\`\`
|
|
756
|
+
|
|
757
|
+
**Step 5 — Run individual templates standalone (without CXG):**
|
|
758
|
+
\`\`\`bash
|
|
759
|
+
python3 .guardlink/cxg-templates/<template-name>.py . --json
|
|
760
|
+
\`\`\`
|
|
761
|
+
|
|
762
|
+
**Expected output artifacts (in \`.guardlink/pentest-findings/\`):**
|
|
763
|
+
- \`guardlink-pentest.json\` — JSON with scan_id, findings array, statistics
|
|
764
|
+
- \`guardlink-pentest.sarif\` — SARIF 2.1.0 for GitHub Advanced Security / CI integration
|
|
765
|
+
- \`guardlink-pentest.html\` — Human-readable HTML report
|
|
766
|
+
- Each finding includes: template_id, severity, title, description, evidence (with request, response, matched_patterns, data), remediation
|
|
767
|
+
- **Evidence must be populated** — a finding with empty evidence (null request, null response, empty data) is a template bug
|
|
768
|
+
- These files are automatically consumed by \`guardlink dashboard\` (Pentest Findings tab) and \`guardlink threat-report\` (pentest context)
|
|
769
|
+
|
|
770
|
+
**Troubleshooting:**
|
|
771
|
+
| Issue | Fix |
|
|
772
|
+
|---|---|
|
|
773
|
+
| \`target is required\` error | Template is missing \`nargs="?"\` on target arg — engine uses env vars, not argv |
|
|
774
|
+
| \`JSON parse error\` | Template prints non-JSON text to stdout in engine mode — wrap all output in \`json.dumps()\` |
|
|
775
|
+
| \`Operation timed out\` | Template takes >30s; add \`--timeout 60s\` to scan command |
|
|
776
|
+
| All templates show 0 findings | Run with \`-vv\` to check for WARN lines; ensure \`local://.\` scope is used for CLI templates |
|
|
777
|
+
| \`guardlink CLI not found\` | Run \`npm install\` in the project root first |
|
|
778
|
+
| Evidence fields are null/empty | Template is passing raw dict without CXG structure — use the \`create_finding\` helper pattern from the Evidence Contract section |
|
|
779
|
+
|
|
780
|
+
4. A "What to expect" section that explains:
|
|
781
|
+
- what a positive finding looks like (JSON with template_id, severity, evidence)
|
|
782
|
+
- what a negative/no-finding run means (code is secure against those specific checks)
|
|
783
|
+
- false-positive caveats and manual verification guidance
|
|
784
|
+
5. Any assumptions requiring human review.`;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Build a prompt for answering freeform user questions about the codebase
|
|
788
|
+
* and GuardLink threat model.
|
|
789
|
+
*/
|
|
790
|
+
export function buildAskPrompt(userQuery, root, model) {
|
|
791
|
+
let modelSummary = 'No threat model parsed yet.';
|
|
792
|
+
let idSummary = '';
|
|
793
|
+
let exposureSummary = '';
|
|
794
|
+
if (model) {
|
|
795
|
+
modelSummary = `Current model: ${model.annotations_parsed} annotations, ${model.exposures.length} exposures, ${(model.confirmed || []).length} confirmed, ${model.mitigations.length} mitigations, ${model.assets.length} assets, ${model.threats.length} threats, ${model.flows.length} flows.`;
|
|
796
|
+
const assetIds = model.assets.filter(a => a.id).slice(0, 30).map(a => `#${a.id}`);
|
|
797
|
+
const threatIds = model.threats.filter(t => t.id).slice(0, 30).map(t => `#${t.id}`);
|
|
798
|
+
const controlIds = model.controls.filter(c => c.id).slice(0, 30).map(c => `#${c.id}`);
|
|
799
|
+
const idLines = [];
|
|
800
|
+
if (assetIds.length)
|
|
801
|
+
idLines.push(`Assets: ${assetIds.join(', ')}`);
|
|
802
|
+
if (threatIds.length)
|
|
803
|
+
idLines.push(`Threats: ${threatIds.join(', ')}`);
|
|
804
|
+
if (controlIds.length)
|
|
805
|
+
idLines.push(`Controls: ${controlIds.join(', ')}`);
|
|
806
|
+
if (idLines.length)
|
|
807
|
+
idSummary = `\n\nKnown IDs:\n${idLines.join('\n')}`;
|
|
808
|
+
const unmitigated = model.exposures.filter((e) => !model.mitigations.some((m) => m.asset === e.asset && m.threat === e.threat));
|
|
809
|
+
if (unmitigated.length > 0) {
|
|
810
|
+
const lines = unmitigated.slice(0, 25).map((e) => `- ${e.asset} -> ${e.threat} [${e.severity || 'unrated'}] (${e.location.file}:${e.location.line})`);
|
|
811
|
+
exposureSummary = `\n\nOpen unmitigated exposures:\n${lines.join('\n')}`;
|
|
812
|
+
if (unmitigated.length > 25) {
|
|
813
|
+
exposureSummary += `\n- ... and ${unmitigated.length - 25} more`;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return `You are a senior AppSec engineer answering questions about a GuardLink-instrumented codebase.
|
|
818
|
+
|
|
819
|
+
## Project Root
|
|
820
|
+
${root}
|
|
821
|
+
|
|
822
|
+
## Current Threat Model Context
|
|
823
|
+
${modelSummary}${idSummary}${exposureSummary}
|
|
824
|
+
|
|
825
|
+
## User Question
|
|
826
|
+
${userQuery}
|
|
827
|
+
|
|
828
|
+
## Required Method
|
|
829
|
+
1. Read relevant source files and configs before answering.
|
|
830
|
+
2. Use GuardLink annotations as guidance, but verify with actual code.
|
|
831
|
+
3. If the question asks about a specific area (e.g. admin portal, API, auth), trace entry points, data flows, and related threats.
|
|
832
|
+
4. If information is missing or ambiguous, say so clearly and list what was checked.
|
|
833
|
+
5. Never invent endpoints, threats, or controls.
|
|
834
|
+
|
|
835
|
+
## Output Format
|
|
836
|
+
- Provide a direct answer first.
|
|
837
|
+
- Then include concise evidence:
|
|
838
|
+
- files/components examined
|
|
839
|
+
- relevant threats/exposures/controls
|
|
840
|
+
- important gaps or unknowns
|
|
841
|
+
- If asked "do we have X threats," include counts and examples with file paths.
|
|
347
842
|
`;
|
|
348
843
|
}
|
|
349
844
|
//# sourceMappingURL=prompts.js.map
|