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/tui/commands.js
CHANGED
|
@@ -22,17 +22,18 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { resolve, basename } from 'node:path';
|
|
24
24
|
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
25
|
-
import { parseProject, findDanglingRefs, findUnmitigatedExposures, findAcceptedWithoutAudit, findAcceptedExposures, clearAnnotations } from '../parser/index.js';
|
|
25
|
+
import { parseProject, findDanglingRefs, findUnmitigatedExposures, findAcceptedWithoutAudit, findAcceptedExposures, clearAnnotations, listFeatures, filterByFeature, getFeatureSummaries } from '../parser/index.js';
|
|
26
26
|
import { initProject, detectProject, promptAgentSelection, syncAgentFiles } from '../init/index.js';
|
|
27
27
|
import { generateReport } from '../report/index.js';
|
|
28
28
|
import { generateDashboardHTML } from '../dashboard/index.js';
|
|
29
29
|
import { computeStats, computeSeverity, computeExposures } from '../dashboard/data.js';
|
|
30
|
-
import { generateThreatReport, serializeModel, listThreatReports, loadThreatReportsForDashboard, FRAMEWORK_LABELS, FRAMEWORK_PROMPTS, buildUserMessage, buildProjectContext, extractCodeSnippets } from '../analyze/index.js';
|
|
30
|
+
import { generateThreatReport, serializeModel, listThreatReports, loadThreatReportsForDashboard, loadPentestData, FRAMEWORK_LABELS, FRAMEWORK_PROMPTS, buildUserMessage, buildProjectContext, extractCodeSnippets } from '../analyze/index.js';
|
|
31
31
|
import { diffModels, formatDiff, parseAtRef } from '../diff/index.js';
|
|
32
32
|
import { generateSarif } from '../analyzer/index.js';
|
|
33
|
+
import { diagnosticIcon } from '../parser/format.js';
|
|
33
34
|
import { C, severityBadge, severityText, severityTextPad, severityOrder, computeGrade, gradeColored, readCodeContext, trunc, bar, fileLink, fileLinkTrunc, cleanCliArtifacts } from './format.js';
|
|
34
35
|
import { resolveLLMConfig, saveTuiConfig, loadTuiConfig } from './config.js';
|
|
35
|
-
import { AGENTS, parseAgentFlag, launchAgent, launchAgentInline, copyToClipboard, buildAnnotatePrompt } from '../agents/index.js';
|
|
36
|
+
import { AGENTS, parseAgentFlag, parseAnnotationModeFlag, launchAgent, launchAgentInline, copyToClipboard, buildAnnotatePrompt } from '../agents/index.js';
|
|
36
37
|
import { describeConfigSource } from '../agents/config.js';
|
|
37
38
|
import { getReviewableExposures, applyReviewAction, summarizeReview } from '../review/index.js';
|
|
38
39
|
import { loadWorkspaceConfig, linkProject, addToWorkspace, removeFromWorkspace, mergeReports, formatMergeSummary, diffMergedReports } from '../workspace/index.js';
|
|
@@ -106,6 +107,7 @@ export function cmdHelp() {
|
|
|
106
107
|
['/workspace', 'Show workspace config and linked repos'],
|
|
107
108
|
['/link <repos...>', 'Link repos into a workspace (--add / --remove)'],
|
|
108
109
|
['/merge <files...>', 'Merge report JSONs into unified dashboard'],
|
|
110
|
+
['/feature [name]', 'List all features or show detail for one'],
|
|
109
111
|
['', ''],
|
|
110
112
|
['/gal', 'GAL annotation language guide'],
|
|
111
113
|
['/help', 'This help'],
|
|
@@ -134,10 +136,12 @@ export function cmdGal() {
|
|
|
134
136
|
console.log(H(' GAL — GuardLink Annotation Language'));
|
|
135
137
|
console.log(H(' ══════════════════════════════════════════════════════════'));
|
|
136
138
|
console.log('');
|
|
137
|
-
console.log(D(' Annotations live in source
|
|
138
|
-
console.log(D(' them
|
|
139
|
+
console.log(D(' Annotations live in source comments or standalone .gal files.'));
|
|
140
|
+
console.log(D(' GuardLink parses them into a live threat model for your codebase.'));
|
|
139
141
|
console.log('');
|
|
140
142
|
console.log(D(' Syntax: @verb subject [preposition object] [-- "description"]'));
|
|
143
|
+
console.log(D(' Inline examples below use comment prefixes; raw .gal files use the same lines without // or #.'));
|
|
144
|
+
console.log(D(' In .gal files, use @source file:<path> line:<n> [symbol:<name>] to anchor following annotations.'));
|
|
141
145
|
console.log('');
|
|
142
146
|
// ── DEFINITIONS ──────────────────────────────────────────────────
|
|
143
147
|
console.log(H(' ── Definitions ─────────────────────────────────────────────'));
|
|
@@ -224,6 +228,16 @@ export function cmdGal() {
|
|
|
224
228
|
console.log(D(' Document a security assumption about an asset.'));
|
|
225
229
|
console.log(EX(' // @assumes api.gateway -- "Upstream WAF filters malformed requests"'));
|
|
226
230
|
console.log('');
|
|
231
|
+
// ── FEATURE TAGGING ──────────────────────────────────────────────
|
|
232
|
+
console.log(H(' ── Feature Tagging ─────────────────────────────────────────'));
|
|
233
|
+
console.log('');
|
|
234
|
+
console.log(` ${V('@feature')} ${K('"Feature Name"')} ${D('[-- "description"]')}`);
|
|
235
|
+
console.log(D(' Tag code with a feature name for filtering reports and dashboards.'));
|
|
236
|
+
console.log(D(' A file can have multiple @feature tags. All annotations in that file'));
|
|
237
|
+
console.log(D(' are associated with the tagged features.'));
|
|
238
|
+
console.log(EX(' // @feature "SSO Login" -- "Single sign-on authentication flow"'));
|
|
239
|
+
console.log(EX(' // @feature "Payment Processing"'));
|
|
240
|
+
console.log('');
|
|
227
241
|
console.log(` ${V('@comment')} ${D('[-- "description"]')}`);
|
|
228
242
|
console.log(D(' Free-form developer security note (no structural effect).'));
|
|
229
243
|
console.log(EX(' // @comment -- "TODO — add rate limiting before v2 launch"'));
|
|
@@ -772,7 +786,9 @@ export async function cmdValidate(ctx) {
|
|
|
772
786
|
if (allDiags.length > 0) {
|
|
773
787
|
console.log('');
|
|
774
788
|
for (const d of allDiags) {
|
|
775
|
-
const
|
|
789
|
+
const icon = diagnosticIcon(d.level);
|
|
790
|
+
const color = d.level === 'fatal' || d.level === 'error' ? C.error : C.warn;
|
|
791
|
+
const prefix = color(` ${icon}`);
|
|
776
792
|
const loc = d.file ? `${fileLink(d.file, d.line, ctx.root)}` : '';
|
|
777
793
|
console.log(`${prefix} ${d.message}${loc ? ` ${C.dim(loc)}` : ''}`);
|
|
778
794
|
}
|
|
@@ -1292,10 +1308,15 @@ export function cmdThreatReports(ctx) {
|
|
|
1292
1308
|
}
|
|
1293
1309
|
// ─── /annotate ───────────────────────────────────────────────────────
|
|
1294
1310
|
export async function cmdAnnotate(args, ctx) {
|
|
1295
|
-
const {
|
|
1311
|
+
const { mode: annotationMode, cleanArgs: argsWithoutMode, error: modeError } = parseAnnotationModeFlag(args);
|
|
1312
|
+
if (modeError) {
|
|
1313
|
+
console.log(C.warn(` ${modeError}`));
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const { agent: flagAgent, cleanArgs } = parseAgentFlag(argsWithoutMode);
|
|
1296
1317
|
if (!cleanArgs.trim()) {
|
|
1297
|
-
console.log(C.warn(' Usage: /annotate <prompt> [--claude-code|--codex|--gemini|--cursor|--windsurf|--clipboard]'));
|
|
1298
|
-
console.log(C.dim(' Example: /annotate "annotate auth endpoints for OWASP Top 10" --claude-code'));
|
|
1318
|
+
console.log(C.warn(' Usage: /annotate <prompt> [--mode inline|external] [--claude-code|--codex|--gemini|--cursor|--windsurf|--clipboard]'));
|
|
1319
|
+
console.log(C.dim(' Example: /annotate "annotate auth endpoints for OWASP Top 10" --mode external --claude-code'));
|
|
1299
1320
|
return;
|
|
1300
1321
|
}
|
|
1301
1322
|
console.log('');
|
|
@@ -1304,7 +1325,7 @@ export async function cmdAnnotate(args, ctx) {
|
|
|
1304
1325
|
if (!agent)
|
|
1305
1326
|
return;
|
|
1306
1327
|
// Build context prompt using shared builder
|
|
1307
|
-
const prompt = buildAnnotatePrompt(cleanArgs.trim(), ctx.root, ctx.model);
|
|
1328
|
+
const prompt = buildAnnotatePrompt(cleanArgs.trim(), ctx.root, ctx.model, annotationMode);
|
|
1308
1329
|
// For terminal agents: foreground spawn (agent takes over terminal)
|
|
1309
1330
|
if (agent.cmd) {
|
|
1310
1331
|
const copied = copyToClipboard(prompt);
|
|
@@ -1559,7 +1580,7 @@ export async function cmdReview(args, ctx) {
|
|
|
1559
1580
|
console.log(` ${C.success('✓')} Marked for remediation — ${result.linesInserted} line(s) written\n`);
|
|
1560
1581
|
}
|
|
1561
1582
|
else {
|
|
1562
|
-
results.push({ exposure: reviewable, action: { decision: 'skip', justification: '' }, linesInserted: 0 });
|
|
1583
|
+
results.push({ exposure: reviewable, action: { decision: 'skip', justification: '' }, linesInserted: 0, targetFile: reviewable.exposure.location.file });
|
|
1563
1584
|
console.log(` ${C.dim('— Skipped')}\n`);
|
|
1564
1585
|
}
|
|
1565
1586
|
}
|
|
@@ -1602,7 +1623,8 @@ export async function cmdDashboard(ctx) {
|
|
|
1602
1623
|
return;
|
|
1603
1624
|
}
|
|
1604
1625
|
const analyses = loadThreatReportsForDashboard(ctx.root);
|
|
1605
|
-
const
|
|
1626
|
+
const pentestData = loadPentestData(ctx.root);
|
|
1627
|
+
const html = generateDashboardHTML(ctx.model, ctx.root, analyses, pentestData);
|
|
1606
1628
|
const outFile = resolve(ctx.root, 'threat-dashboard.html');
|
|
1607
1629
|
const { writeFile } = await import('node:fs/promises');
|
|
1608
1630
|
await writeFile(outFile, html);
|
|
@@ -1643,6 +1665,70 @@ export function cmdWorkspace(ctx) {
|
|
|
1643
1665
|
console.log(C.dim(' /merge to combine reports · /link --add to add a repo · /link --remove to remove'));
|
|
1644
1666
|
console.log('');
|
|
1645
1667
|
}
|
|
1668
|
+
// ─── /feature ────────────────────────────────────────────────────────
|
|
1669
|
+
export function cmdFeature(args, ctx) {
|
|
1670
|
+
if (!ctx.model) {
|
|
1671
|
+
console.log(C.warn(' No threat model. Run /parse first.'));
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
const features = listFeatures(ctx.model);
|
|
1675
|
+
if (features.length === 0) {
|
|
1676
|
+
console.log(' No @feature annotations found.');
|
|
1677
|
+
console.log(C.dim(' Tag code with: // @feature "Feature Name" -- "description"'));
|
|
1678
|
+
console.log('');
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
const name = args.trim();
|
|
1682
|
+
if (name) {
|
|
1683
|
+
// Show detail for specific feature
|
|
1684
|
+
const filtered = filterByFeature(ctx.model, [name]);
|
|
1685
|
+
const total = filtered.assets.length + filtered.threats.length +
|
|
1686
|
+
filtered.controls.length + filtered.mitigations.length + filtered.exposures.length +
|
|
1687
|
+
filtered.confirmed.length + filtered.flows.length + filtered.boundaries.length;
|
|
1688
|
+
if (total === 0) {
|
|
1689
|
+
console.log(C.warn(` No annotations found for feature "${name}".`));
|
|
1690
|
+
console.log(` Available: ${features.map(f => `"${f}"`).join(', ')}`);
|
|
1691
|
+
console.log('');
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
console.log(` Feature: ${C.bold(`"${name}"`)}`);
|
|
1695
|
+
console.log('');
|
|
1696
|
+
console.log(` Assets: ${filtered.assets.length}`);
|
|
1697
|
+
console.log(` Threats: ${filtered.threats.length}`);
|
|
1698
|
+
console.log(` Controls: ${filtered.controls.length}`);
|
|
1699
|
+
console.log(` Mitigations: ${filtered.mitigations.length}`);
|
|
1700
|
+
console.log(` Exposures: ${filtered.exposures.length}`);
|
|
1701
|
+
if (filtered.confirmed.length > 0)
|
|
1702
|
+
console.log(` Confirmed: ${filtered.confirmed.length} 🔴`);
|
|
1703
|
+
console.log(` Flows: ${filtered.flows.length}`);
|
|
1704
|
+
console.log(` Boundaries: ${filtered.boundaries.length}`);
|
|
1705
|
+
if (filtered.exposures.length > 0) {
|
|
1706
|
+
console.log('');
|
|
1707
|
+
console.log(` ${C.bold('Exposures:')}`);
|
|
1708
|
+
for (const e of filtered.exposures) {
|
|
1709
|
+
console.log(` ${e.asset} → ${e.threat} ${severityBadge(e.severity || 'unset')} (${fileLink(e.location.file, e.location.line)})`);
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
console.log('');
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
// List all features
|
|
1716
|
+
const summaries = getFeatureSummaries(ctx.model);
|
|
1717
|
+
console.log(` ${C.bold('Features:')}`);
|
|
1718
|
+
console.log('');
|
|
1719
|
+
for (const s of summaries) {
|
|
1720
|
+
const files = s.files.length === 1 ? '1 file' : `${s.files.length} files`;
|
|
1721
|
+
const exposureInfo = s.exposures > 0 ? C.red(` | ${s.exposures} exposure(s)`) : '';
|
|
1722
|
+
const confirmedInfo = s.confirmed > 0 ? C.red(` | ${s.confirmed} confirmed`) : '';
|
|
1723
|
+
console.log(` ${C.cyan(`"${s.name}"`)} (${files}, ${s.annotations} annotations${exposureInfo}${confirmedInfo})`);
|
|
1724
|
+
for (const f of s.files) {
|
|
1725
|
+
console.log(` → ${C.dim(f)}`);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
console.log('');
|
|
1729
|
+
console.log(C.dim(` ${features.length} feature(s) total. Use /feature <name> for detail.`));
|
|
1730
|
+
console.log('');
|
|
1731
|
+
}
|
|
1646
1732
|
// ─── /link ───────────────────────────────────────────────────────────
|
|
1647
1733
|
export async function cmdLink(args, ctx) {
|
|
1648
1734
|
const parts = args.trim().split(/\s+/).filter(Boolean);
|