guardlink 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +344 -0
  4. package/dist/agents/config.d.ts +46 -0
  5. package/dist/agents/config.d.ts.map +1 -0
  6. package/dist/agents/config.js +189 -0
  7. package/dist/agents/config.js.map +1 -0
  8. package/dist/agents/index.d.ts +24 -0
  9. package/dist/agents/index.d.ts.map +1 -0
  10. package/dist/agents/index.js +42 -0
  11. package/dist/agents/index.js.map +1 -0
  12. package/dist/agents/launcher.d.ts +54 -0
  13. package/dist/agents/launcher.d.ts.map +1 -0
  14. package/dist/agents/launcher.js +152 -0
  15. package/dist/agents/launcher.js.map +1 -0
  16. package/dist/agents/prompts.d.ts +14 -0
  17. package/dist/agents/prompts.d.ts.map +1 -0
  18. package/dist/agents/prompts.js +120 -0
  19. package/dist/agents/prompts.js.map +1 -0
  20. package/dist/analyze/index.d.ts +80 -0
  21. package/dist/analyze/index.d.ts.map +1 -0
  22. package/dist/analyze/index.js +306 -0
  23. package/dist/analyze/index.js.map +1 -0
  24. package/dist/analyze/llm.d.ts +52 -0
  25. package/dist/analyze/llm.d.ts.map +1 -0
  26. package/dist/analyze/llm.js +295 -0
  27. package/dist/analyze/llm.js.map +1 -0
  28. package/dist/analyze/prompts.d.ts +14 -0
  29. package/dist/analyze/prompts.d.ts.map +1 -0
  30. package/dist/analyze/prompts.js +205 -0
  31. package/dist/analyze/prompts.js.map +1 -0
  32. package/dist/analyzer/index.d.ts +5 -0
  33. package/dist/analyzer/index.d.ts.map +1 -0
  34. package/dist/analyzer/index.js +5 -0
  35. package/dist/analyzer/index.js.map +1 -0
  36. package/dist/analyzer/sarif.d.ts +84 -0
  37. package/dist/analyzer/sarif.d.ts.map +1 -0
  38. package/dist/analyzer/sarif.js +149 -0
  39. package/dist/analyzer/sarif.js.map +1 -0
  40. package/dist/cli/index.d.ts +25 -0
  41. package/dist/cli/index.d.ts.map +1 -0
  42. package/dist/cli/index.js +821 -0
  43. package/dist/cli/index.js.map +1 -0
  44. package/dist/dashboard/data.d.ts +52 -0
  45. package/dist/dashboard/data.d.ts.map +1 -0
  46. package/dist/dashboard/data.js +93 -0
  47. package/dist/dashboard/data.js.map +1 -0
  48. package/dist/dashboard/diagrams.d.ts +25 -0
  49. package/dist/dashboard/diagrams.d.ts.map +1 -0
  50. package/dist/dashboard/diagrams.js +243 -0
  51. package/dist/dashboard/diagrams.js.map +1 -0
  52. package/dist/dashboard/generate.d.ts +17 -0
  53. package/dist/dashboard/generate.d.ts.map +1 -0
  54. package/dist/dashboard/generate.js +1258 -0
  55. package/dist/dashboard/generate.js.map +1 -0
  56. package/dist/dashboard/index.d.ts +7 -0
  57. package/dist/dashboard/index.d.ts.map +1 -0
  58. package/dist/dashboard/index.js +7 -0
  59. package/dist/dashboard/index.js.map +1 -0
  60. package/dist/diff/engine.d.ts +51 -0
  61. package/dist/diff/engine.d.ts.map +1 -0
  62. package/dist/diff/engine.js +153 -0
  63. package/dist/diff/engine.js.map +1 -0
  64. package/dist/diff/format.d.ts +10 -0
  65. package/dist/diff/format.d.ts.map +1 -0
  66. package/dist/diff/format.js +111 -0
  67. package/dist/diff/format.js.map +1 -0
  68. package/dist/diff/git.d.ts +24 -0
  69. package/dist/diff/git.d.ts.map +1 -0
  70. package/dist/diff/git.js +85 -0
  71. package/dist/diff/git.js.map +1 -0
  72. package/dist/diff/index.d.ts +7 -0
  73. package/dist/diff/index.d.ts.map +1 -0
  74. package/dist/diff/index.js +7 -0
  75. package/dist/diff/index.js.map +1 -0
  76. package/dist/index.d.ts +20 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +17 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/init/detect.d.ts +42 -0
  81. package/dist/init/detect.d.ts.map +1 -0
  82. package/dist/init/detect.js +185 -0
  83. package/dist/init/detect.js.map +1 -0
  84. package/dist/init/index.d.ts +39 -0
  85. package/dist/init/index.d.ts.map +1 -0
  86. package/dist/init/index.js +228 -0
  87. package/dist/init/index.js.map +1 -0
  88. package/dist/init/picker.d.ts +32 -0
  89. package/dist/init/picker.d.ts.map +1 -0
  90. package/dist/init/picker.js +105 -0
  91. package/dist/init/picker.js.map +1 -0
  92. package/dist/init/templates.d.ts +25 -0
  93. package/dist/init/templates.d.ts.map +1 -0
  94. package/dist/init/templates.js +263 -0
  95. package/dist/init/templates.js.map +1 -0
  96. package/dist/mcp/index.d.ts +12 -0
  97. package/dist/mcp/index.d.ts.map +1 -0
  98. package/dist/mcp/index.js +18 -0
  99. package/dist/mcp/index.js.map +1 -0
  100. package/dist/mcp/lookup.d.ts +27 -0
  101. package/dist/mcp/lookup.d.ts.map +1 -0
  102. package/dist/mcp/lookup.js +282 -0
  103. package/dist/mcp/lookup.js.map +1 -0
  104. package/dist/mcp/server.d.ts +41 -0
  105. package/dist/mcp/server.d.ts.map +1 -0
  106. package/dist/mcp/server.js +388 -0
  107. package/dist/mcp/server.js.map +1 -0
  108. package/dist/mcp/suggest.d.ts +35 -0
  109. package/dist/mcp/suggest.d.ts.map +1 -0
  110. package/dist/mcp/suggest.js +268 -0
  111. package/dist/mcp/suggest.js.map +1 -0
  112. package/dist/parser/comment-strip.d.ts +15 -0
  113. package/dist/parser/comment-strip.d.ts.map +1 -0
  114. package/dist/parser/comment-strip.js +76 -0
  115. package/dist/parser/comment-strip.js.map +1 -0
  116. package/dist/parser/index.d.ts +10 -0
  117. package/dist/parser/index.d.ts.map +1 -0
  118. package/dist/parser/index.js +9 -0
  119. package/dist/parser/index.js.map +1 -0
  120. package/dist/parser/normalize.d.ts +22 -0
  121. package/dist/parser/normalize.d.ts.map +1 -0
  122. package/dist/parser/normalize.js +42 -0
  123. package/dist/parser/normalize.js.map +1 -0
  124. package/dist/parser/parse-file.d.ts +18 -0
  125. package/dist/parser/parse-file.d.ts.map +1 -0
  126. package/dist/parser/parse-file.js +68 -0
  127. package/dist/parser/parse-file.js.map +1 -0
  128. package/dist/parser/parse-line.d.ts +21 -0
  129. package/dist/parser/parse-line.d.ts.map +1 -0
  130. package/dist/parser/parse-line.js +230 -0
  131. package/dist/parser/parse-line.js.map +1 -0
  132. package/dist/parser/parse-project.d.ts +31 -0
  133. package/dist/parser/parse-project.d.ts.map +1 -0
  134. package/dist/parser/parse-project.js +281 -0
  135. package/dist/parser/parse-project.js.map +1 -0
  136. package/dist/report/index.d.ts +6 -0
  137. package/dist/report/index.d.ts.map +1 -0
  138. package/dist/report/index.js +6 -0
  139. package/dist/report/index.js.map +1 -0
  140. package/dist/report/mermaid.d.ts +15 -0
  141. package/dist/report/mermaid.d.ts.map +1 -0
  142. package/dist/report/mermaid.js +260 -0
  143. package/dist/report/mermaid.js.map +1 -0
  144. package/dist/report/report.d.ts +16 -0
  145. package/dist/report/report.d.ts.map +1 -0
  146. package/dist/report/report.js +211 -0
  147. package/dist/report/report.js.map +1 -0
  148. package/dist/tui/commands.d.ts +42 -0
  149. package/dist/tui/commands.d.ts.map +1 -0
  150. package/dist/tui/commands.js +1216 -0
  151. package/dist/tui/commands.js.map +1 -0
  152. package/dist/tui/config.d.ts +27 -0
  153. package/dist/tui/config.d.ts.map +1 -0
  154. package/dist/tui/config.js +27 -0
  155. package/dist/tui/config.js.map +1 -0
  156. package/dist/tui/format.d.ts +63 -0
  157. package/dist/tui/format.d.ts.map +1 -0
  158. package/dist/tui/format.js +253 -0
  159. package/dist/tui/format.js.map +1 -0
  160. package/dist/tui/index.d.ts +18 -0
  161. package/dist/tui/index.d.ts.map +1 -0
  162. package/dist/tui/index.js +470 -0
  163. package/dist/tui/index.js.map +1 -0
  164. package/dist/tui/input.d.ts +63 -0
  165. package/dist/tui/input.d.ts.map +1 -0
  166. package/dist/tui/input.js +454 -0
  167. package/dist/tui/input.js.map +1 -0
  168. package/dist/types/index.d.ts +254 -0
  169. package/dist/types/index.d.ts.map +1 -0
  170. package/dist/types/index.js +6 -0
  171. package/dist/types/index.js.map +1 -0
  172. package/package.json +97 -0
@@ -0,0 +1,268 @@
1
+ /**
2
+ * GuardLink Suggest — Annotation suggestion engine.
3
+ *
4
+ * Analyzes code for patterns that warrant security annotations:
5
+ * - Function names suggesting security-relevant operations
6
+ * - Dangerous imports/patterns (eval, exec, SQL, file I/O, crypto)
7
+ * - HTTP handlers, auth checks, input parsing
8
+ * - Missing annotations on files that handle sensitive data
9
+ *
10
+ * Designed for both file-based and diff-based analysis (§8.2).
11
+ *
12
+ * @exposes #suggest to #path-traversal [high] cwe:CWE-22 -- "Reads files from user-specified paths for analysis"
13
+ * @exposes #suggest to #prompt-injection [medium] cwe:CWE-77 -- "Suggestion output may be fed back to LLM agents"
14
+ * @accepts #prompt-injection on #suggest -- "Suggestions are intended for LLM consumption"
15
+ * @mitigates #suggest against #path-traversal using #path-validation -- "join() combines root with relative file paths"
16
+ * @flows #mcp -> #suggest via suggestAnnotations -- "MCP tool passes file path from agent request"
17
+ * @flows #suggest -> #mcp via suggestions -- "Suggestions returned to calling agent"
18
+ */
19
+ import { readFileSync, existsSync } from 'node:fs';
20
+ import { join } from 'node:path';
21
+ export async function suggestAnnotations(opts) {
22
+ const suggestions = [];
23
+ if (opts.diff) {
24
+ suggestFromDiff(opts.diff, opts.model, suggestions);
25
+ }
26
+ else if (opts.file) {
27
+ const fullPath = join(opts.root, opts.file);
28
+ if (existsSync(fullPath)) {
29
+ const content = readFileSync(fullPath, 'utf-8');
30
+ suggestFromFile(opts.file, content, opts.model, suggestions);
31
+ }
32
+ }
33
+ // Deduplicate and sort by confidence
34
+ const seen = new Set();
35
+ const confOrder = { high: 0, medium: 1, low: 2 };
36
+ return suggestions
37
+ .filter(s => {
38
+ const key = `${s.file}:${s.line}:${s.annotation}`;
39
+ if (seen.has(key))
40
+ return false;
41
+ seen.add(key);
42
+ return true;
43
+ })
44
+ .sort((a, b) => confOrder[a.confidence] - confOrder[b.confidence]);
45
+ }
46
+ const PATTERNS = [
47
+ // SQL / database
48
+ {
49
+ regex: /(?:execute|query|raw_sql|cursor\.execute|\.query\(|knex\.|sequelize\.|prisma\.)\s*\(/i,
50
+ category: 'exposure',
51
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #sqli with Insufficient input validation`,
52
+ reason: 'Database query detected — potential SQL injection if inputs are not parameterized',
53
+ confidence: 'high',
54
+ },
55
+ // Command execution
56
+ {
57
+ regex: /(?:exec|spawn|execSync|child_process|subprocess|os\.system|os\.popen|Runtime\.exec)\s*\(/i,
58
+ category: 'exposure',
59
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #cmd-injection with Unsanitized command arguments`,
60
+ reason: 'Command execution detected — potential command injection',
61
+ confidence: 'high',
62
+ },
63
+ // File I/O with user input
64
+ {
65
+ regex: /(?:readFile|writeFile|open\(|fopen|fs\.read|Path\.resolve|path\.join).*(?:req\.|request\.|params|query|body)/i,
66
+ category: 'exposure',
67
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #path-traversal with User-controlled file path`,
68
+ reason: 'File operation with user-influenced path — potential path traversal',
69
+ confidence: 'high',
70
+ },
71
+ // eval / dynamic code
72
+ {
73
+ regex: /(?:eval\(|new\s+Function\(|exec\(|compile\(|setInterval\(|setTimeout\().*(?:req|input|user|param|body)/i,
74
+ category: 'exposure',
75
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #code-injection with Dynamic code execution`,
76
+ reason: 'Dynamic code execution with potential user input',
77
+ confidence: 'high',
78
+ },
79
+ // Auth/login handlers
80
+ {
81
+ regex: /(?:def\s+login|function\s+login|authenticate|verify_password|check_credentials|signIn)/i,
82
+ category: 'asset',
83
+ annotation: (_m, f) => `@asset ${assetFromFile(f)} -- "Authentication handler"`,
84
+ reason: 'Authentication-related function — should be declared as security-relevant asset',
85
+ confidence: 'medium',
86
+ },
87
+ // Input validation / sanitization
88
+ {
89
+ regex: /(?:sanitize|validate|escape|htmlEscape|xss|dompurify|bleach|strip_tags)/i,
90
+ category: 'mitigation',
91
+ annotation: (_m, f) => `@control Input_Validation (#input-validation) -- "Sanitizes user input"`,
92
+ reason: 'Input validation/sanitization detected — should be documented as a control',
93
+ confidence: 'medium',
94
+ },
95
+ // CORS configuration
96
+ {
97
+ regex: /(?:cors|Access-Control-Allow-Origin|allowed_origins)/i,
98
+ category: 'mitigation',
99
+ annotation: (_m, f) => `@control CORS_Policy (#cors-policy) -- "Restricts cross-origin requests"`,
100
+ reason: 'CORS configuration detected',
101
+ confidence: 'medium',
102
+ },
103
+ // Rate limiting
104
+ {
105
+ regex: /(?:rate.?limit|throttle|RateLimiter|express-rate-limit|slowapi)/i,
106
+ category: 'mitigation',
107
+ annotation: (_m, f) => `@control Rate_Limiting (#rate-limit) -- "Limits request frequency"`,
108
+ reason: 'Rate limiting detected',
109
+ confidence: 'medium',
110
+ },
111
+ // Crypto / hashing
112
+ {
113
+ regex: /(?:bcrypt|scrypt|argon2|pbkdf2|hashlib|crypto\.createHash|md5|sha256)/i,
114
+ category: 'mitigation',
115
+ annotation: (_m, f) => `@control Crypto_Hashing (#crypto-hash) -- "Cryptographic hashing for sensitive data"`,
116
+ reason: 'Cryptographic hashing detected',
117
+ confidence: 'medium',
118
+ },
119
+ // HTTP handlers
120
+ {
121
+ regex: /(?:@app\.(?:get|post|put|delete|patch)|router\.(?:get|post|put|delete)|@GetMapping|@PostMapping|@RequestMapping)/i,
122
+ category: 'flow',
123
+ annotation: (_m, f) => `@handles ${assetFromFile(f)} -- "HTTP request handler"`,
124
+ reason: 'HTTP endpoint handler — represents an entry point for user data',
125
+ confidence: 'medium',
126
+ },
127
+ // Secrets / credentials
128
+ {
129
+ regex: /(?:API_KEY|SECRET_KEY|PASSWORD|TOKEN|PRIVATE_KEY|AWS_ACCESS|DATABASE_URL)\s*[=:]/i,
130
+ category: 'data_handling',
131
+ annotation: (_m, f) => `@data ${assetFromFile(f)} stores secrets -- "Contains credentials or API keys"`,
132
+ reason: 'Hardcoded secret or credential reference detected',
133
+ confidence: 'high',
134
+ },
135
+ // Deserialization
136
+ {
137
+ regex: /(?:pickle\.load|yaml\.load\(|yaml\.unsafe_load|JSON\.parse|deserialize|unmarshal|ObjectInputStream)/i,
138
+ category: 'exposure',
139
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #unsafe-deser with Deserialization of untrusted data`,
140
+ reason: 'Deserialization detected — potential unsafe deserialization if input is untrusted',
141
+ confidence: 'medium',
142
+ },
143
+ // SSRF patterns
144
+ {
145
+ regex: /(?:fetch|requests\.get|urllib|http\.get|axios|HttpClient).*(?:req\.|request\.|params|query|body|url)/i,
146
+ category: 'exposure',
147
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #ssrf with User-controlled URL`,
148
+ reason: 'HTTP request with user-influenced URL — potential SSRF',
149
+ confidence: 'medium',
150
+ },
151
+ // Template rendering (XSS)
152
+ {
153
+ regex: /(?:render_template|innerHTML|dangerouslySetInnerHTML|v-html|ng-bind-html|\|safe\b|mark_safe)/i,
154
+ category: 'exposure',
155
+ annotation: (_m, f) => `@exposes ${assetFromFile(f)} to #xss with Unescaped output rendering`,
156
+ reason: 'Unsafe HTML rendering detected — potential XSS',
157
+ confidence: 'high',
158
+ },
159
+ ];
160
+ // ─── File analysis ───────────────────────────────────────────────────
161
+ function suggestFromFile(file, content, model, out) {
162
+ // Skip definition files and non-source
163
+ if (file.includes('.guardlink/') || file.includes('node_modules/'))
164
+ return;
165
+ const lines = content.split('\n');
166
+ // Check if file already has annotations
167
+ const hasAnnotations = lines.some(l => /@(?:asset|threat|control|exposes|mitigates|accepts|flows|boundary|handles|comment|data|shield)\b/.test(l));
168
+ for (let i = 0; i < lines.length; i++) {
169
+ const line = lines[i];
170
+ for (const pattern of PATTERNS) {
171
+ const match = line.match(pattern.regex);
172
+ if (match) {
173
+ out.push({
174
+ file,
175
+ line: i + 1,
176
+ annotation: pattern.annotation(match, file),
177
+ reason: pattern.reason,
178
+ confidence: hasAnnotations ? pattern.confidence : lowerConfidence(pattern.confidence),
179
+ category: pattern.category,
180
+ });
181
+ }
182
+ }
183
+ }
184
+ // Post-pass: suggest @comment on security-relevant functions with no nearby annotations or suggestions
185
+ const FUNC_RE = /(?:(?:export\s+)?(?:async\s+)?function\s+(\w+)|(?:export\s+)?class\s+(\w+)|def\s+(\w+)|fn\s+(\w+)|func\s+(\w+))/i;
186
+ const SEC_KEYWORDS = /auth|login|cred|token|secret|password|session|crypto|encrypt|decrypt|hash|key|cert|ssl|tls|api|admin|pay|billing|charge|invoice/i;
187
+ const suggestedLines = new Set(out.map(s => s.line));
188
+ for (let i = 0; i < lines.length; i++) {
189
+ const funcMatch = lines[i].match(FUNC_RE);
190
+ if (!funcMatch)
191
+ continue;
192
+ const funcName = funcMatch[1] || funcMatch[2] || funcMatch[3] || funcMatch[4] || funcMatch[5];
193
+ const lineNum = i + 1;
194
+ // Skip if already has a suggestion or annotation within 3 lines above
195
+ const hasNearby = Array.from({ length: 4 }, (_, k) => lineNum - k).some(ln => suggestedLines.has(ln) || (ln > 0 && ln <= lines.length && /@\w+/.test(lines[ln - 1])));
196
+ if (hasNearby)
197
+ continue;
198
+ // Only suggest for functions with security-relevant names or in security-relevant file paths
199
+ if (SEC_KEYWORDS.test(funcName) || SEC_KEYWORDS.test(file)) {
200
+ out.push({
201
+ file,
202
+ line: lineNum,
203
+ annotation: `@comment -- "TODO: Document security relevance of ${funcName}"`,
204
+ reason: `Security-relevant function '${funcName}' has no annotation — add at least @comment`,
205
+ confidence: 'low',
206
+ category: 'asset',
207
+ });
208
+ }
209
+ }
210
+ }
211
+ // ─── Diff analysis ───────────────────────────────────────────────────
212
+ function suggestFromDiff(diff, model, out) {
213
+ // Parse unified diff format
214
+ let currentFile = '';
215
+ let lineNum = 0;
216
+ for (const line of diff.split('\n')) {
217
+ // File header: +++ b/path/to/file.ts
218
+ const fileMatch = line.match(/^\+\+\+\s+b\/(.+)/);
219
+ if (fileMatch) {
220
+ currentFile = fileMatch[1];
221
+ continue;
222
+ }
223
+ // Hunk header: @@ -old,count +new,count @@
224
+ const hunkMatch = line.match(/^@@\s+-\d+(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/);
225
+ if (hunkMatch) {
226
+ lineNum = parseInt(hunkMatch[1], 10);
227
+ continue;
228
+ }
229
+ // Added lines (+ prefix)
230
+ if (line.startsWith('+') && !line.startsWith('+++')) {
231
+ const content = line.slice(1);
232
+ for (const pattern of PATTERNS) {
233
+ const match = content.match(pattern.regex);
234
+ if (match) {
235
+ out.push({
236
+ file: currentFile,
237
+ line: lineNum,
238
+ annotation: pattern.annotation(match, currentFile),
239
+ reason: `[New code] ${pattern.reason}`,
240
+ confidence: pattern.confidence,
241
+ category: pattern.category,
242
+ });
243
+ }
244
+ }
245
+ lineNum++;
246
+ }
247
+ else if (!line.startsWith('-')) {
248
+ // Context line (no prefix) — increment line counter
249
+ lineNum++;
250
+ }
251
+ // Removed lines (- prefix) — don't increment
252
+ }
253
+ }
254
+ // ─── Helpers ─────────────────────────────────────────────────────────
255
+ function assetFromFile(file) {
256
+ // Convert file path to a reasonable asset name suggestion
257
+ const base = file
258
+ .replace(/\.[^.]+$/, '') // Remove extension
259
+ .replace(/^src\/|^lib\/|^app\//, '') // Strip common prefixes
260
+ .replace(/\//g, '.'); // Dots for path segments
261
+ return base.split('.').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('.');
262
+ }
263
+ function lowerConfidence(c) {
264
+ if (c === 'high')
265
+ return 'medium';
266
+ return 'low';
267
+ }
268
+ //# sourceMappingURL=suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.js","sourceRoot":"","sources":["../../src/mcp/suggest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Comment prefix stripping per §2.9.
3
+ * Strips the host language's comment prefix to expose the annotation text.
4
+ */
5
+ /**
6
+ * Strip comment prefix from a single line, returning the inner text
7
+ * or null if the line is not a comment.
8
+ */
9
+ export declare function stripCommentPrefix(line: string): string | null;
10
+ /**
11
+ * Detect file's primary comment style from extension.
12
+ * Used for multi-line continuation detection.
13
+ */
14
+ export declare function commentStyleForExt(ext: string): string;
15
+ //# sourceMappingURL=comment-strip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-strip.d.ts","sourceRoot":"","sources":["../../src/parser/comment-strip.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+C9D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkBtD"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Comment prefix stripping per §2.9.
3
+ * Strips the host language's comment prefix to expose the annotation text.
4
+ */
5
+ /**
6
+ * Strip comment prefix from a single line, returning the inner text
7
+ * or null if the line is not a comment.
8
+ */
9
+ export function stripCommentPrefix(line) {
10
+ const trimmed = line.trimStart();
11
+ // Single-line styles (order matters — longer prefixes first)
12
+ const singlePrefixes = [
13
+ '//', // C-family, Rust, Go, JS, TS
14
+ '#', // Python, Ruby, Bash, YAML, Terraform
15
+ '--', // Haskell, Lua, SQL, Ada
16
+ '%', // LaTeX, Erlang, MATLAB
17
+ ';', // Lisp, Clojure, Assembly
18
+ 'REM ', // Batch (with trailing space)
19
+ 'REM\t',
20
+ "'", // VBA, VB.NET
21
+ ];
22
+ for (const prefix of singlePrefixes) {
23
+ if (trimmed.startsWith(prefix)) {
24
+ return trimmed.slice(prefix.length).trimStart();
25
+ }
26
+ }
27
+ // Block comment line (already inside a block)
28
+ // Strip leading * (Javadoc-style) or bare text in block
29
+ if (trimmed.startsWith('*') && !trimmed.startsWith('*/')) {
30
+ return trimmed.slice(1).trimStart();
31
+ }
32
+ // HTML/XML comment: <!-- ... -->
33
+ const htmlMatch = trimmed.match(/^<!--\s*(.*?)\s*-->$/);
34
+ if (htmlMatch)
35
+ return htmlMatch[1];
36
+ // Opening block comment on same line: /* ... */ or /* ...
37
+ const blockOpenClose = trimmed.match(/^\/\*\s*(.*?)\s*\*\/$/);
38
+ if (blockOpenClose)
39
+ return blockOpenClose[1];
40
+ const blockOpen = trimmed.match(/^\/\*\s*(.*)$/);
41
+ if (blockOpen)
42
+ return blockOpen[1].trimStart();
43
+ // Haskell block: {- ... -}
44
+ const haskellBlock = trimmed.match(/^\{-\s*(.*?)\s*-\}$/);
45
+ if (haskellBlock)
46
+ return haskellBlock[1];
47
+ // OCaml/Pascal: (* ... *)
48
+ const ocamlBlock = trimmed.match(/^\(\*\s*(.*?)\s*\*\)$/);
49
+ if (ocamlBlock)
50
+ return ocamlBlock[1];
51
+ return null;
52
+ }
53
+ /**
54
+ * Detect file's primary comment style from extension.
55
+ * Used for multi-line continuation detection.
56
+ */
57
+ export function commentStyleForExt(ext) {
58
+ const map = {
59
+ '.ts': '//', '.tsx': '//', '.js': '//', '.jsx': '//',
60
+ '.java': '//', '.c': '//', '.cpp': '//', '.cc': '//',
61
+ '.cs': '//', '.go': '//', '.rs': '//', '.swift': '//',
62
+ '.kt': '//', '.scala': '//', '.dart': '//',
63
+ '.py': '#', '.rb': '#', '.sh': '#', '.bash': '#',
64
+ '.yml': '#', '.yaml': '#', '.tf': '#', '.r': '#',
65
+ '.ex': '#', '.exs': '#', '.nim': '#', '.pl': '#',
66
+ '.hs': '--', '.lua': '--', '.sql': '--', '.ada': '--',
67
+ '.html': '<!--', '.xml': '<!--', '.svg': '<!--',
68
+ '.css': '/*',
69
+ '.tex': '%', '.erl': '%', '.m': '%',
70
+ '.lisp': ';', '.cl': ';', '.clj': ';', '.asm': ';',
71
+ '.bat': 'REM', '.cmd': 'REM',
72
+ '.vb': "'", '.bas': "'",
73
+ };
74
+ return map[ext.toLowerCase()] || '//';
75
+ }
76
+ //# sourceMappingURL=comment-strip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-strip.js","sourceRoot":"","sources":["../../src/parser/comment-strip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * GuardLink Parser — Public API
3
+ */
4
+ export { parseFile, parseString } from './parse-file.js';
5
+ export { parseProject } from './parse-project.js';
6
+ export type { ParseProjectOptions } from './parse-project.js';
7
+ export { parseLine } from './parse-line.js';
8
+ export { normalizeName, resolveSeverity, unescapeDescription } from './normalize.js';
9
+ export { stripCommentPrefix, commentStyleForExt } from './comment-strip.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * GuardLink Parser — Public API
3
+ */
4
+ export { parseFile, parseString } from './parse-file.js';
5
+ export { parseProject } from './parse-project.js';
6
+ export { parseLine } from './parse-line.js';
7
+ export { normalizeName, resolveSeverity, unescapeDescription } from './normalize.js';
8
+ export { stripCommentPrefix, commentStyleForExt } from './comment-strip.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Name normalization per §2.10 of the GuardLink spec.
3
+ *
4
+ * Algorithm:
5
+ * 1. Apply Unicode NFKC normalization
6
+ * 2. Convert to lowercase
7
+ * 3. Replace whitespace → underscore
8
+ * 4. Replace hyphens → underscore
9
+ * 5. Collapse consecutive underscores
10
+ * 6. Strip leading/trailing underscores
11
+ */
12
+ export declare function normalizeName(name: string): string;
13
+ /**
14
+ * Severity alias resolution: P0→critical, P1→high, etc.
15
+ */
16
+ export declare function resolveSeverity(raw: string): 'critical' | 'high' | 'medium' | 'low' | undefined;
17
+ /**
18
+ * Unescape description string per §2.11.
19
+ * Handles \" → " and \\\\ → \\
20
+ */
21
+ export declare function unescapeDescription(raw: string): string;
22
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/parser/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAQ/F;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Name normalization per §2.10 of the GuardLink spec.
3
+ *
4
+ * Algorithm:
5
+ * 1. Apply Unicode NFKC normalization
6
+ * 2. Convert to lowercase
7
+ * 3. Replace whitespace → underscore
8
+ * 4. Replace hyphens → underscore
9
+ * 5. Collapse consecutive underscores
10
+ * 6. Strip leading/trailing underscores
11
+ */
12
+ export function normalizeName(name) {
13
+ return name
14
+ .normalize('NFKC')
15
+ .toLowerCase()
16
+ .replace(/[\s\t\u00A0]+/g, '_') // whitespace → underscore
17
+ .replace(/-+/g, '_') // hyphens → underscore
18
+ .replace(/_+/g, '_') // collapse consecutive
19
+ .replace(/^_|_$/g, ''); // strip leading/trailing
20
+ }
21
+ /**
22
+ * Severity alias resolution: P0→critical, P1→high, etc.
23
+ */
24
+ export function resolveSeverity(raw) {
25
+ const map = {
26
+ p0: 'critical', critical: 'critical',
27
+ p1: 'high', high: 'high',
28
+ p2: 'medium', medium: 'medium',
29
+ p3: 'low', low: 'low',
30
+ };
31
+ return map[raw.toLowerCase()];
32
+ }
33
+ /**
34
+ * Unescape description string per §2.11.
35
+ * Handles \" → " and \\\\ → \\
36
+ */
37
+ export function unescapeDescription(raw) {
38
+ return raw
39
+ .replace(/\\"/g, '"')
40
+ .replace(/\\\\/g, '\\');
41
+ }
42
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/parser/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI;SACR,SAAS,CAAC,MAAM,CAAC;SACjB,WAAW,EAAE;SACb,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAE,0BAA0B;SAC1D,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAc,uBAAuB;SACxD,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAc,uBAAuB;SACxD,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAW,yBAAyB;AAC/D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,GAAG,GAA2D;QAClE,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU;QACpC,EAAE,EAAE,MAAM,EAAM,IAAI,EAAE,MAAM;QAC5B,EAAE,EAAE,QAAQ,EAAI,MAAM,EAAE,QAAQ;QAChC,EAAE,EAAE,KAAK,EAAO,GAAG,EAAE,KAAK;KAC3B,CAAC;IACF,OAAO,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,GAAG;SACP,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * GuardLink — File-level parser.
3
+ * Reads source files and extracts all GuardLink annotations.
4
+ *
5
+ * @exposes #parser to #path-traversal [high] cwe:CWE-22 -- "Reads files from disk using provided file path"
6
+ * @comment -- "File paths are controlled by parse-project glob results, not direct user input"
7
+ */
8
+ import type { ParseResult } from '../types/index.js';
9
+ /**
10
+ * Parse a single file and return all annotations found.
11
+ */
12
+ export declare function parseFile(filePath: string): Promise<ParseResult>;
13
+ /**
14
+ * Parse a string of source code and return all annotations found.
15
+ * Useful for testing without file I/O.
16
+ */
17
+ export declare function parseString(content: string, filePath?: string): ParseResult;
18
+ //# sourceMappingURL=parse-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-file.d.ts","sourceRoot":"","sources":["../../src/parser/parse-file.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAA+B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKlF;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGtE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAkB,GAAG,WAAW,CAgDtF"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * GuardLink — File-level parser.
3
+ * Reads source files and extracts all GuardLink annotations.
4
+ *
5
+ * @exposes #parser to #path-traversal [high] cwe:CWE-22 -- "Reads files from disk using provided file path"
6
+ * @comment -- "File paths are controlled by parse-project glob results, not direct user input"
7
+ */
8
+ import { readFile } from 'node:fs/promises';
9
+ import { stripCommentPrefix } from './comment-strip.js';
10
+ import { parseLine } from './parse-line.js';
11
+ import { unescapeDescription } from './normalize.js';
12
+ /**
13
+ * Parse a single file and return all annotations found.
14
+ */
15
+ export async function parseFile(filePath) {
16
+ const content = await readFile(filePath, 'utf-8');
17
+ return parseString(content, filePath);
18
+ }
19
+ /**
20
+ * Parse a string of source code and return all annotations found.
21
+ * Useful for testing without file I/O.
22
+ */
23
+ export function parseString(content, filePath = '<input>') {
24
+ const lines = content.split('\n');
25
+ const annotations = [];
26
+ const diagnostics = [];
27
+ let lastAnnotation = null;
28
+ for (let i = 0; i < lines.length; i++) {
29
+ const lineNum = i + 1; // 1-indexed
30
+ const rawLine = lines[i];
31
+ // Strip comment prefix
32
+ const inner = stripCommentPrefix(rawLine);
33
+ if (inner === null) {
34
+ lastAnnotation = null;
35
+ continue;
36
+ }
37
+ // Check for continuation line: -- "..."
38
+ const contMatch = inner.match(/^--\s*"((?:[^"\\]|\\.)*)"/);
39
+ if (contMatch && lastAnnotation) {
40
+ // Append to last annotation's description
41
+ const contDesc = unescapeDescription(contMatch[1]);
42
+ if (lastAnnotation.description) {
43
+ lastAnnotation.description += ' ' + contDesc;
44
+ }
45
+ else {
46
+ lastAnnotation.description = contDesc;
47
+ }
48
+ continue;
49
+ }
50
+ // Try to parse as annotation
51
+ const location = { file: filePath, line: lineNum };
52
+ const result = parseLine(inner, location);
53
+ if (result.annotation) {
54
+ annotations.push(result.annotation);
55
+ lastAnnotation = result.annotation;
56
+ }
57
+ else {
58
+ if (result.diagnostic) {
59
+ diagnostics.push(result.diagnostic);
60
+ }
61
+ if (!result.isContinuation) {
62
+ lastAnnotation = null;
63
+ }
64
+ }
65
+ }
66
+ return { annotations, diagnostics, files_parsed: 1 };
67
+ }
68
+ //# sourceMappingURL=parse-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-file.js","sourceRoot":"","sources":["../../src/parser/parse-file.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB;IAC9C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClD,OAAO,WAAW,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,WAAmB,SAAS;IACvE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,WAAW,GAAsB,EAAE,CAAC;IAC1C,IAAI,cAAc,GAAsB,IAAI,CAAC;IAE7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAE,YAAY;QACpC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAEzB,uBAAuB;QACvB,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,cAAc,GAAG,IAAI,CAAC;YACtB,SAAS;QACX,CAAC;QAED,wCAAwC;QACxC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC3D,IAAI,SAAS,IAAI,cAAc,EAAE,CAAC;YAChC,0CAA0C;YAC1C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YACnD,IAAI,cAAc,CAAC,WAAW,EAAE,CAAC;gBAC/B,cAAc,CAAC,WAAW,IAAI,GAAG,GAAG,QAAQ,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,WAAW,GAAG,QAAQ,CAAC;YACxC,CAAC;YACD,SAAS;QACX,CAAC;QAED,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACpC,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBACtB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACtC,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC3B,cAAc,GAAG,IAAI,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * GuardLink — Line-level annotation parser.
3
+ * Parses a single comment line into a typed Annotation.
4
+ *
5
+ * @exposes #parser to #redos [medium] cwe:CWE-1333 -- "Complex regex patterns run against untrusted annotation content"
6
+ * @mitigates #parser against #redos using #regex-anchoring -- "All patterns use ^ anchor and defined character classes"
7
+ * @comment -- "PATTERNS object contains all annotation regex; patterns are pre-compiled for performance"
8
+ */
9
+ import type { Annotation, ParseDiagnostic, SourceLocation } from '../types/index.js';
10
+ export interface ParseLineResult {
11
+ annotation: Annotation | null;
12
+ diagnostic: ParseDiagnostic | null;
13
+ isContinuation: boolean;
14
+ }
15
+ /**
16
+ * Parse a single annotation line (after comment prefix has been stripped).
17
+ * Returns the typed annotation, a diagnostic if parsing failed, or null if
18
+ * the line is not an annotation.
19
+ */
20
+ export declare function parseLine(text: string, location: SourceLocation): ParseLineResult;
21
+ //# sourceMappingURL=parse-line.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-line.d.ts","sourceRoot":"","sources":["../../src/parser/parse-line.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EAAE,cAAc,EAChC,MAAM,mBAAmB,CAAC;AAsE3B,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,cAAc,GACvB,eAAe,CA8KjB"}