Skip to content

Commit dabbdc0

Browse files
author
merge-queue-bot
committed
Merge PR #685: security: retire resolved findings from 2026-06-19 SARIF files
2 parents 1599c9f + 7d2a6ce commit dabbdc0

5 files changed

Lines changed: 34 additions & 374 deletions

File tree

docs/security/2026-06-19-full-repo-audit/findings.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"title": "<?include?> directive follows within-workspace symlinks to files outside the workspace root",
1313
"severity": "high",
1414
"confidence": "confirmed",
15+
"status": "fixed",
16+
"fixed_by": "PR #670 — replaced os.DirFS with os.OpenRoot(rootDir).FS() via lint.OpenRootFS in internal/lint/file.go; os.OpenRoot enforces RESOLVE_BENEATH semantics, refusing symlinks whose targets escape the workspace root",
1517
"surface": "directive",
1618
"cwe": "CWE-73",
1719
"location": {
@@ -33,6 +35,8 @@
3335
"title": "<?catalog glob:?> follows within-workspace symlinks to files outside the workspace root",
3436
"severity": "high",
3537
"confidence": "confirmed",
38+
"status": "fixed",
39+
"fixed_by": "PR #670 — same fix as S001: all RootFS construction sites use lint.OpenRootFS backed by os.OpenRoot; os.OpenRoot.FS() denies GlobWalk traversal through symlinks that target paths outside the workspace root",
3640
"surface": "directive",
3741
"cwe": "CWE-73",
3842
"location": {
@@ -54,6 +58,8 @@
5458
"title": "CLI engine-runner goroutines lack per-goroutine recover() — rule panic crashes the process",
5559
"severity": "medium",
5660
"confidence": "confirmed",
61+
"status": "fixed",
62+
"fixed_by": "PR #670 — added defer recover() at the top of lintFile (internal/engine/runner.go:396-400); panics in any rule's Check or Fix are caught, converted to InternalError diagnostics, and the process continues linting the remaining files",
5763
"surface": "cli",
5864
"cwe": "CWE-390",
5965
"location": {
@@ -71,6 +77,8 @@
7177
"title": "<?catalog glob:?> has no per-directive file-count cap — large repos can cause OOM",
7278
"severity": "low",
7379
"confidence": "confirmed",
80+
"status": "fixed",
81+
"fixed_by": "PR #670 — added maxCatalogMatches = 10_000 constant in internal/rules/catalog/rule.go:40; resolveGlobMatchesFrom returns errCatalogCapExceeded and emits a user-facing diagnostic once the cap is exceeded",
7482
"surface": "directive",
7583
"cwe": "CWE-400",
7684
"location": {
@@ -88,6 +96,8 @@
8896
"title": "hasSymlinkAncestor skips ancestor scan when cwd is unresolvable and no .git root exists",
8997
"severity": "low",
9098
"confidence": "confirmed",
99+
"status": "fixed",
100+
"fixed_by": "PR #670 — changed hasSymlinkAncestor in internal/lint/files.go:274-277 to return an error when ancestorStopBoundary is empty (fail-closed); the caller now skips the path rather than silently passing the symlink check",
91101
"surface": "cli",
92102
"cwe": "CWE-61",
93103
"location": {
@@ -105,6 +115,8 @@
105115
"title": "<?include?> path validation does not explicitly reject URL schemes — incidentally safe but fragile",
106116
"severity": "info",
107117
"confidence": "confirmed",
118+
"status": "fixed",
119+
"fixed_by": "PR #670 — added explicit strings.Contains(file, \"://\") URL-scheme check in validateIncludeDirective (internal/rules/include/rule.go:140-143); URL-scheme paths are now rejected with a diagnostic rather than incidentally failing at os.DirFS",
108120
"surface": "directive",
109121
"cwe": "CWE-918",
110122
"location": {
@@ -122,6 +134,8 @@
122134
"title": "githooksync rule reads hook and gitattributes files with unbounded os.ReadFile",
123135
"severity": "info",
124136
"confidence": "confirmed",
137+
"status": "fixed",
138+
"fixed_by": "PR #670 — replaced all four os.ReadFile calls in internal/rules/githooksync/rule.go (lines 187, 261, 307, 398) with bytelimit.ReadFileLimited(path, hookMaxReadBytes) where hookMaxReadBytes = 1024*1024",
125139
"surface": "git",
126140
"cwe": "CWE-400",
127141
"location": {

docs/security/2026-06-19-full-repo-audit/findings.sarif

Lines changed: 1 addition & 254 deletions
Original file line numberDiff line numberDiff line change
@@ -151,260 +151,7 @@
151151
]
152152
}
153153
},
154-
"results": [
155-
{
156-
"ruleId": "S001",
157-
"ruleIndex": 0,
158-
"level": "error",
159-
"message": {
160-
"text": "<?include?> directive follows within-workspace symlinks to files outside the workspace root"
161-
},
162-
"locations": [
163-
{
164-
"physicalLocation": {
165-
"artifactLocation": {
166-
"uri": "internal/rules/include/rule.go"
167-
},
168-
"region": {
169-
"startLine": 253,
170-
"endLine": 267
171-
}
172-
}
173-
},
174-
{
175-
"physicalLocation": {
176-
"artifactLocation": {
177-
"uri": "internal/lint/file.go"
178-
},
179-
"region": {
180-
"startLine": 286,
181-
"endLine": 286
182-
}
183-
}
184-
},
185-
{
186-
"physicalLocation": {
187-
"artifactLocation": {
188-
"uri": "pkg/mdsmith/workspace.go"
189-
},
190-
"region": {
191-
"startLine": 100,
192-
"endLine": 100
193-
}
194-
}
195-
}
196-
],
197-
"properties": {
198-
"confidence": "confirmed",
199-
"severity": "high"
200-
}
201-
},
202-
{
203-
"ruleId": "S002",
204-
"ruleIndex": 1,
205-
"level": "error",
206-
"message": {
207-
"text": "<?catalog glob:?> follows within-workspace symlinks to files outside the workspace root"
208-
},
209-
"locations": [
210-
{
211-
"physicalLocation": {
212-
"artifactLocation": {
213-
"uri": "internal/rules/catalog/rule.go"
214-
},
215-
"region": {
216-
"startLine": 893,
217-
"endLine": 906
218-
}
219-
}
220-
},
221-
{
222-
"physicalLocation": {
223-
"artifactLocation": {
224-
"uri": "internal/lint/file.go"
225-
},
226-
"region": {
227-
"startLine": 286,
228-
"endLine": 286
229-
}
230-
}
231-
},
232-
{
233-
"physicalLocation": {
234-
"artifactLocation": {
235-
"uri": "pkg/mdsmith/workspace.go"
236-
},
237-
"region": {
238-
"startLine": 100,
239-
"endLine": 100
240-
}
241-
}
242-
}
243-
],
244-
"properties": {
245-
"confidence": "confirmed",
246-
"severity": "high"
247-
}
248-
},
249-
{
250-
"ruleId": "S003",
251-
"ruleIndex": 2,
252-
"level": "warning",
253-
"message": {
254-
"text": "CLI engine-runner goroutines lack per-goroutine recover() — rule panic crashes the process"
255-
},
256-
"locations": [
257-
{
258-
"physicalLocation": {
259-
"artifactLocation": {
260-
"uri": "internal/engine/runner.go"
261-
},
262-
"region": {
263-
"startLine": 353,
264-
"endLine": 370
265-
}
266-
}
267-
}
268-
],
269-
"properties": {
270-
"confidence": "confirmed",
271-
"severity": "medium"
272-
}
273-
},
274-
{
275-
"ruleId": "S004",
276-
"ruleIndex": 3,
277-
"level": "note",
278-
"message": {
279-
"text": "<?catalog glob:?> has no per-directive file-count cap — large repos can cause OOM"
280-
},
281-
"locations": [
282-
{
283-
"physicalLocation": {
284-
"artifactLocation": {
285-
"uri": "internal/rules/catalog/rule.go"
286-
},
287-
"region": {
288-
"startLine": 888,
289-
"endLine": 920
290-
}
291-
}
292-
}
293-
],
294-
"properties": {
295-
"confidence": "confirmed",
296-
"severity": "low"
297-
}
298-
},
299-
{
300-
"ruleId": "S005",
301-
"ruleIndex": 4,
302-
"level": "note",
303-
"message": {
304-
"text": "hasSymlinkAncestor skips ancestor scan when cwd is unresolvable and no .git root exists"
305-
},
306-
"locations": [
307-
{
308-
"physicalLocation": {
309-
"artifactLocation": {
310-
"uri": "internal/lint/files.go"
311-
},
312-
"region": {
313-
"startLine": 244,
314-
"endLine": 246
315-
}
316-
}
317-
}
318-
],
319-
"properties": {
320-
"confidence": "confirmed",
321-
"severity": "low"
322-
}
323-
},
324-
{
325-
"ruleId": "S006",
326-
"ruleIndex": 5,
327-
"level": "note",
328-
"message": {
329-
"text": "<?include?> path validation does not explicitly reject URL schemes — incidentally safe but fragile"
330-
},
331-
"locations": [
332-
{
333-
"physicalLocation": {
334-
"artifactLocation": {
335-
"uri": "internal/rules/include/rule.go"
336-
},
337-
"region": {
338-
"startLine": 130,
339-
"endLine": 170
340-
}
341-
}
342-
}
343-
],
344-
"properties": {
345-
"confidence": "confirmed",
346-
"severity": "info"
347-
}
348-
},
349-
{
350-
"ruleId": "S007",
351-
"ruleIndex": 6,
352-
"level": "note",
353-
"message": {
354-
"text": "githooksync rule reads hook and gitattributes files with unbounded os.ReadFile"
355-
},
356-
"locations": [
357-
{
358-
"physicalLocation": {
359-
"artifactLocation": {
360-
"uri": "internal/rules/githooksync/rule.go"
361-
},
362-
"region": {
363-
"startLine": 180,
364-
"endLine": 180
365-
}
366-
}
367-
},
368-
{
369-
"physicalLocation": {
370-
"artifactLocation": {
371-
"uri": "internal/rules/githooksync/rule.go"
372-
},
373-
"region": {
374-
"startLine": 249,
375-
"endLine": 249
376-
}
377-
}
378-
},
379-
{
380-
"physicalLocation": {
381-
"artifactLocation": {
382-
"uri": "internal/rules/githooksync/rule.go"
383-
},
384-
"region": {
385-
"startLine": 295,
386-
"endLine": 295
387-
}
388-
}
389-
},
390-
{
391-
"physicalLocation": {
392-
"artifactLocation": {
393-
"uri": "internal/rules/githooksync/rule.go"
394-
},
395-
"region": {
396-
"startLine": 386,
397-
"endLine": 386
398-
}
399-
}
400-
}
401-
],
402-
"properties": {
403-
"confidence": "confirmed",
404-
"severity": "info"
405-
}
406-
}
407-
]
154+
"results": []
408155
}
409156
]
410157
}

docs/security/2026-06-19-lsp-vscode-audit/findings.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
"title": "VS Code extension: mdsmith.kinds.resolve and mdsmith.kinds.why run in untrusted workspaces without a trust gate",
116116
"severity": "low",
117117
"confidence": "confirmed",
118+
"status": "fixed",
119+
"fixed_by": "PR #670 — added isWorkspaceTrusted to commandPalette when-conditions (package.json:125-130) and isTrusted() guard at top of runKindsResolve/runKindsWhy (kinds.ts:42,58)",
118120
"surface": "vscode",
119121
"cwe": "CWE-284",
120122
"location": {
@@ -130,13 +132,15 @@
130132
"description": "The commandPalette 'when' conditions for mdsmith.kinds.resolve and mdsmith.kinds.why omit isWorkspaceTrusted (package.json:125-130). When invoked in an untrusted workspace, wiring.ts:829/838 calls runKindsResolve/runKindsWhy with no isTrusted() guard, spawning 'mdsmith kinds resolve/why --json -- file' (virtual-doc.ts:72). The binary is the bundled binary (mdsmith.path is restricted to default). The subcommands are read-only: they output JSON and exit; no files are written, no recipes are executed. The binary reads .mdsmith.yml to derive kind assignments, so a hostile config can inject attacker-controlled text into the virtual document pane but cannot execute code.",
131133
"impact": "Low. In untrusted workspaces a hostile .mdsmith.yml causes mdsmith to output attacker-controlled text into the virtual document pane. No file writes or code execution. The virtual document is read-only in the editor. The binary cannot be redirected (mdsmith.path restricted).",
132134
"repro": "Clone a hostile repo with a malformed .mdsmith.yml. Open a .md file. Run 'mdsmith: Explain Rule on This File' from the palette. The virtual document displays content derived from the hostile config.",
133-
"remediation": "Add 'isWorkspaceTrusted' to the commandPalette 'when' conditions for mdsmith.kinds.why and mdsmith.kinds.resolve, and add an isTrusted() guard at the top of runKindsResolve/runKindsWhy — matching the pattern used by fix-workspace, init, and merge-driver. These are informational commands so the restriction is minor friction."
135+
"remediation": "Fixed. Added 'isWorkspaceTrusted' to commandPalette when-conditions (package.json:125-130) and isTrusted() guard at top of runKindsResolve/runKindsWhy in kinds.ts."
134136
},
135137
{
136138
"id": "S007",
137139
"title": "VS Code extension: mdsmith-rule: virtual document content provider runs without trust gate",
138140
"severity": "low",
139141
"confidence": "confirmed",
142+
"status": "fixed",
143+
"fixed_by": "PR #670 — added isTrusted() guard in registerTextDocumentContentProvider for RULE_SCHEME (wiring.ts:915)",
140144
"surface": "vscode",
141145
"cwe": "CWE-284",
142146
"location": {
@@ -151,7 +155,7 @@
151155
"description": "The mdsmith-rule: TextDocumentContentProvider (wiring.ts:906-910) runs 'mdsmith help rule <id>' to render an embedded rule README. It has no trust gate. The id is validated against /^MDS\\d+$/i (rule-doc.ts:35) before being passed as a CLI argument, so arbitrary arg injection is prevented. The binary is the bundled binary (mdsmith.path restricted). 'mdsmith help rule' reads READMEs embedded in the binary and makes no network call, no file writes. The `OPEN_RULE_DOC_COMMAND` constant is triggered by rewritten hover links in MarkdownString blocks with isTrusted set only to `{enabledCommands: ['OPEN_RULE_DOC_COMMAND']}` (rule-doc.ts:115), preventing other commands from being injected. Risk is minimal but the pattern is inconsistent with the trust model.",
152156
"impact": "Very low. The worst case is spawning the bundled mdsmith binary with a validated rule ID argument. The output is embedded README text displayed in a read-only virtual document.",
153157
"repro": "Hover over a diagnostic in an untrusted workspace. Click the rewritten rule-docs link. 'mdsmith help rule MDS0XX' runs.",
154-
"remediation": "Low priority. If desired, add a trust check before spawning in provideRuleDocContent / fetchRuleDocContent. Alternatively, embed the rule READMEs directly in the extension's JS bundle to eliminate the spawn entirely for this read-only use case."
158+
"remediation": "Fixed. Added if (!isTrusted()) return Promise.resolve('') at the top of the RULE_SCHEME content provider in wiring.ts:915."
155159
}
156160
]
157161
}

0 commit comments

Comments
 (0)