Skip to content

Commit 1e6c614

Browse files
shivasuryaclaude
andcommitted
feat: global taint flow detection + source/sink output display
- Fix summaryConfirmsFlow to accept ParamToSink as flow confirmation, enabling detection from HTTP handler functions (void, no return taint) that call a source internally and pass taint to callees. - Fix global dedup to key by sink only (not source+sink), preventing the same sink from appearing once per source handler. - Add SourceSnippet/SourceLocation to EnrichedDetection; text output now shows Source: <file:line> + snippet and Sink: <file:line> + snippet for Taint-Global findings. - Rewrite command_injection.py and sql_injection.py as L1 QueryType rules; remove L4 path_traversal.py (bare calls, no source constraint). - Add OLLAMA-SEC-001/002/003 rules targeting exec, filepath, and outbound HTTP sinks from gin/net-http sources. - Add security_flows test fixture for verifying global inter-procedural taint detection end-to-end. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 77222e7 commit 1e6c614

File tree

13 files changed

+280
-263
lines changed

13 files changed

+280
-263
lines changed

rules/golang/command_injection.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,26 @@
9393
- OWASP A03:2021 Injection: https://owasp.org/Top10/A03_2021-Injection/
9494
"""
9595

96-
from codepathfinder import rule, calls
97-
98-
@rule(
99-
id="GO-SEC-002",
100-
severity="CRITICAL",
101-
cwe="CWE-78",
102-
owasp="A03:2021"
103-
)
96+
from codepathfinder.go_rule import GoGinContext, GoHTTPRequest, GoOSExec
97+
from codepathfinder import flows
98+
from codepathfinder.presets import PropagationPresets
99+
from rules.go_decorators import go_rule
100+
101+
102+
@go_rule(id="GO-SEC-002", severity="CRITICAL", cwe="CWE-78", owasp="A03:2021")
104103
def go_command_injection():
105-
"""Detects OS command execution that may be vulnerable to injection.
106-
Flags calls to exec.Command and exec.CommandContext."""
107-
return calls("*Command", "*CommandContext")
104+
"""HTTP/Gin request input reaches os/exec — command injection.
105+
L1: GoGinContext + GoHTTPRequest sources, GoOSExec sink — both sides QueryType."""
106+
return flows(
107+
from_sources=[
108+
GoGinContext.method("Param", "Query", "PostForm", "GetRawData",
109+
"ShouldBindJSON", "BindJSON", "GetHeader"),
110+
GoHTTPRequest.method("FormValue", "PostFormValue"),
111+
GoHTTPRequest.attr("Body", "URL.Path", "URL.RawQuery"),
112+
],
113+
to_sinks=[
114+
GoOSExec.method("Command", "CommandContext"),
115+
],
116+
propagates_through=PropagationPresets.standard(),
117+
scope="global",
118+
)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
OLLAMA-SEC-001: HTTP / Gin Request Input Reaching OS Command Execution
3+
4+
Sources: gin.Context params/body, net/http.Request body
5+
Sinks: exec.Command, exec.CommandContext
6+
7+
Variants targeted:
8+
- Model name from /api/pull, /api/chat JSON body → runner subprocess args
9+
(llm/server.go, x/imagegen/server.go, x/mlxrunner/client.go)
10+
- Tool call command string → bash -c (x/tools/bash.go)
11+
- Path param → exec arg chain
12+
13+
L1: GoGinContext + GoHTTPRequest sources, GoOSExec sink — both sides QueryType.
14+
"""
15+
16+
from codepathfinder.go_rule import GoGinContext, GoHTTPRequest, GoOSExec
17+
from codepathfinder import flows
18+
from codepathfinder.presets import PropagationPresets
19+
from rules.go_decorators import go_rule
20+
21+
22+
@go_rule(id="OLLAMA-SEC-001", severity="CRITICAL", cwe="CWE-78", owasp="A03:2021")
23+
def ollama_http_to_exec():
24+
"""HTTP/Gin request input reaches os/exec — command injection variants."""
25+
return flows(
26+
from_sources=[
27+
GoGinContext.method("Param", "Query", "PostForm", "GetRawData",
28+
"ShouldBindJSON", "BindJSON", "GetHeader"),
29+
GoHTTPRequest.method("FormValue", "PostFormValue"),
30+
GoHTTPRequest.attr("Body", "URL.Path", "URL.RawQuery", "Header"),
31+
],
32+
to_sinks=[
33+
GoOSExec.method("Command", "CommandContext"),
34+
],
35+
sanitized_by=[],
36+
propagates_through=PropagationPresets.standard(),
37+
scope="global",
38+
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
OLLAMA-SEC-002: HTTP / Gin Request Input Reaching File System Operations
3+
4+
Sources: gin.Context params/body, net/http.Request
5+
Sinks: filepath.Join, os.Create, os.Open, os.OpenFile, os.WriteFile, os.ReadFile, os.MkdirAll
6+
7+
Variants targeted:
8+
- Blob digest from URL path c.Param("digest") → manifest path → os.Create (CVE-2024-37032 regression)
9+
- Model name from JSON body → manifest filepath construction
10+
- req.Files paths from CreateHandler → os.Open
11+
- Zip entry name → filepath.Join (ZipSlip: CVE-2024-7773, CVE-2024-45436)
12+
13+
L1: GoGinContext + GoHTTPRequest sources, GoFilepath + GoOS sinks — both sides QueryType.
14+
"""
15+
16+
from codepathfinder.go_rule import GoGinContext, GoHTTPRequest, GoOS, GoFilepath
17+
from codepathfinder import flows
18+
from codepathfinder.presets import PropagationPresets
19+
from rules.go_decorators import go_rule
20+
21+
22+
@go_rule(id="OLLAMA-SEC-002", severity="HIGH", cwe="CWE-22", owasp="A01:2021")
23+
def ollama_http_to_filepath():
24+
"""HTTP/Gin request input reaches file system operations — path traversal variants."""
25+
return flows(
26+
from_sources=[
27+
# c.Param("digest") is the confirmed direct user-controlled source
28+
# that flows into manifest.BlobsPath() and file ops in ollama.
29+
# ShouldBindJSON(&req) fills structs — struct field propagation
30+
# is not yet covered by standard presets.
31+
GoGinContext.method("Param", "Query", "PostForm", "GetHeader"),
32+
GoHTTPRequest.method("FormValue", "PostFormValue"),
33+
GoHTTPRequest.attr("URL.Path", "URL.RawQuery"),
34+
],
35+
to_sinks=[
36+
GoFilepath.method("Join"),
37+
GoOS.method("Create", "Open", "OpenFile", "WriteFile",
38+
"ReadFile", "MkdirAll", "Remove", "Rename"),
39+
],
40+
sanitized_by=[],
41+
propagates_through=PropagationPresets.standard(),
42+
scope="global",
43+
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
OLLAMA-SEC-003: HTTP / Gin Request Input Reaching Outbound HTTP Calls (SSRF)
3+
4+
Sources: gin.Context params/body, net/http.Request
5+
Sinks: http.Client.Do, http.Get, http.Post, http.NewRequest, http.NewRequestWithContext
6+
7+
Variants targeted:
8+
- Model RemoteHost field (from JSON body) → outbound HTTP call (SSRF via model config)
9+
- Registry URL built from user-supplied model name → download request
10+
- WWW-Authenticate realm forwarded to attacker host (CVE-2025-51471 variant)
11+
- Cloud proxy path constructed from user model reference
12+
13+
L1: GoGinContext + GoHTTPRequest sources, GoHTTPClient sink — both sides QueryType.
14+
"""
15+
16+
from codepathfinder.go_rule import GoGinContext, GoHTTPRequest, GoHTTPClient
17+
from codepathfinder import flows
18+
from codepathfinder.presets import PropagationPresets
19+
from rules.go_decorators import go_rule
20+
21+
22+
@go_rule(id="OLLAMA-SEC-003", severity="HIGH", cwe="CWE-918", owasp="A10:2021")
23+
def ollama_http_to_outbound():
24+
"""HTTP/Gin request input reaches outbound HTTP calls — SSRF variants."""
25+
return flows(
26+
from_sources=[
27+
GoGinContext.method("Param", "Query", "PostForm", "GetRawData",
28+
"ShouldBindJSON", "BindJSON", "GetHeader"),
29+
GoHTTPRequest.method("FormValue", "PostFormValue"),
30+
GoHTTPRequest.attr("Body", "URL.Path", "URL.RawQuery", "Header"),
31+
],
32+
to_sinks=[
33+
GoHTTPClient.method("Do", "Get", "Post", "Head",
34+
"NewRequest", "NewRequestWithContext"),
35+
],
36+
sanitized_by=[],
37+
propagates_through=PropagationPresets.standard(),
38+
scope="global",
39+
)

rules/golang/path_traversal.py

Lines changed: 0 additions & 133 deletions
This file was deleted.

0 commit comments

Comments
 (0)