mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-21 20:18:06 +02:00
Dynamic (#77)
This commit is contained in:
parent
55247b7fcd
commit
991c84a1eb
1464 changed files with 225448 additions and 1985 deletions
|
|
@ -45,14 +45,14 @@
|
|||
"notes": "Runtime.getRuntime().exec(command) with deserialized input; AST pattern correctly matches"
|
||||
},
|
||||
{
|
||||
"rule_id": "java.xss.getwriter_print",
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "MEDIUM",
|
||||
"must_not_match": true,
|
||||
"line_range": [
|
||||
11,
|
||||
11
|
||||
],
|
||||
"notes": "response.getWriter().println(\"Done\") — constant string, Layer B suppresses (regression guard)"
|
||||
"notes": "response.getWriter().println(\"Done\") — constant string, must NOT raise reflected-XSS (Cap::HTML_ESCAPE). Regression guard retargeted from the retired java.xss.getwriter_print AST pattern to the taint sink that now owns reflected XSS."
|
||||
},
|
||||
{
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
|
|
|
|||
|
|
@ -80,14 +80,14 @@
|
|||
"notes": "source at 11:9 (request.getParameter(\"input\")) flows through SQL query (line 17) into result set output at out.println(rs.getString(1)); second-order taint via tainted query results"
|
||||
},
|
||||
{
|
||||
"rule_id": "java.xss.getwriter_print",
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "MEDIUM",
|
||||
"must_not_match": true,
|
||||
"line_range": [
|
||||
26,
|
||||
26
|
||||
],
|
||||
"notes": "response.getWriter().println(new String(data)) — file-read data, Layer B suppresses (regression guard)"
|
||||
"notes": "response.getWriter().println(new String(data)) — file-read bytes, not reflected request input, must NOT raise reflected-XSS (Cap::HTML_ESCAPE). Regression guard retargeted from the retired java.xss.getwriter_print AST pattern to the taint sink that now owns reflected XSS."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,7 @@
|
|||
"must_match": true,
|
||||
"line_range": [5, 12],
|
||||
"evidence_contains": [],
|
||||
"notes": "catch(Exception e) binds e as tainted; e flows to println sink via catch parameter"
|
||||
},
|
||||
{
|
||||
"rule_id": "java.xss.getwriter_print",
|
||||
"severity": "MEDIUM",
|
||||
"must_match": true,
|
||||
"line_range": [10, 10],
|
||||
"evidence_contains": [],
|
||||
"notes": "response.getWriter().println() in catch block — AST pattern detects potential XSS via error response"
|
||||
"notes": "catch(Exception e) binds e as tainted; e flows to response.getWriter().println at line 10 — reflected XSS via error response. Replaces the retired java.xss.getwriter_print AST pattern: reflected XSS is now a taint sink (Sink(Cap::HTML_ESCAPE)), so this is taint-confirmed rather than flagged on every writer call."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
19
tests/fixtures/real_world/java/taint/cmdi_deadbranch_const_safe.expect.json
vendored
Normal file
19
tests/fixtures/real_world/java/taint/cmdi_deadbranch_const_safe.expect.json
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "Dead-branch constant condition (OWASP Benchmark cmdi non-vulnerable shape). `(7*42) - num > 200` with num=86 is 208 > 200 — always true — so `bar` is the constant string and the `else bar = param` arm is statically dead. The constant-branch fold (src/ssa/const_prop.rs::fold_constant_branches) evaluates the captured CondArith tree, prunes the dead edge, and drops the tainted phi operand AND neutralises the dead block so copy-prop cannot alias `bar`<->`param`. Result: `r.exec(cmd + bar)` carries no taint. Asserts NO taint finding fires (strict_unexpected promotes any taint-unsanitised-flow to a hard failure).",
|
||||
"tags": [
|
||||
"taint",
|
||||
"cmdi",
|
||||
"servlet",
|
||||
"runtime",
|
||||
"dead-branch",
|
||||
"const-fold",
|
||||
"precision"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"strict_unexpected": [
|
||||
"taint-unsanitised-flow"
|
||||
],
|
||||
"expected": []
|
||||
}
|
||||
27
tests/fixtures/real_world/java/taint/cmdi_deadbranch_const_safe.java
vendored
Normal file
27
tests/fixtures/real_world/java/taint/cmdi_deadbranch_const_safe.java
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import java.io.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
// Dead-branch constant condition (OWASP Benchmark cmdi non-vulnerable shape).
|
||||
// The guard `(7*42) - num > 200` is `294 - 86 = 208 > 200`, i.e. ALWAYS true,
|
||||
// so `bar` is provably the constant string and the tainted `else` arm
|
||||
// (`bar = param`) is unreachable. The constant-branch fold
|
||||
// (`fold_constant_branches`) must prune the dead edge and drop the tainted
|
||||
// phi operand so `r.exec(cmd + bar)` carries no attacker data — NO finding.
|
||||
public class DeadBranchConstSafe extends HttpServlet {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String param = request.getHeader("vector");
|
||||
|
||||
String bar;
|
||||
int num = 86;
|
||||
if ((7 * 42) - num > 200) {
|
||||
bar = "This_should_always_happen";
|
||||
} else {
|
||||
bar = param;
|
||||
}
|
||||
|
||||
String cmd = "echo ";
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process p = r.exec(cmd + bar);
|
||||
}
|
||||
}
|
||||
32
tests/fixtures/real_world/java/taint/cmdi_deadbranch_param_vuln.expect.json
vendored
Normal file
32
tests/fixtures/real_world/java/taint/cmdi_deadbranch_param_vuln.expect.json
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"description": "Dead-branch constant condition with VULNERABLE polarity. `(500/42) + num > 200` is `11 + 196 = 207 > 200` (integer division) — always true — and the TRUE arm assigns the tainted `param`, so the reachable branch carries taint and only the `else bar = \"...\"` arm is dead. The constant-branch fold must prune the DEAD else edge while keeping the live `bar = param`, so the command-injection finding at `r.exec(cmd + bar)` MUST still fire. Zero-false-negative guard: it proves the fold never prunes the reachable (tainted) arm.",
|
||||
"tags": [
|
||||
"taint",
|
||||
"cmdi",
|
||||
"servlet",
|
||||
"runtime",
|
||||
"dead-branch",
|
||||
"const-fold",
|
||||
"no-false-negative"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"strict_unexpected": [
|
||||
"taint-unsanitised-flow"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "HIGH",
|
||||
"must_match": true,
|
||||
"line_range": [
|
||||
26,
|
||||
26
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "request.getHeader (line 15) flows into bar on the always-taken true arm (line 21), then into r.exec at line 26. Exactly one finding survives.",
|
||||
"max_count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
28
tests/fixtures/real_world/java/taint/cmdi_deadbranch_param_vuln.java
vendored
Normal file
28
tests/fixtures/real_world/java/taint/cmdi_deadbranch_param_vuln.java
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import java.io.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
// Dead-branch constant condition, VULNERABLE polarity (OWASP Benchmark cmdi
|
||||
// vulnerable shape). The guard `(500/42) + num > 200` is `11 + 196 = 207 > 200`
|
||||
// using integer division — ALWAYS true — and the TRUE arm assigns the tainted
|
||||
// `param`. So the live branch carries taint and the `else bar = "never"` arm is
|
||||
// dead. The constant-branch fold must prune the DEAD (else) edge and keep the
|
||||
// reachable tainted `bar = param`, so `r.exec(cmd + bar)` MUST still fire. This
|
||||
// is the zero-false-negative guard: the fold must never prune the live arm.
|
||||
public class DeadBranchParamVuln extends HttpServlet {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String param = request.getHeader("vector");
|
||||
|
||||
String bar;
|
||||
int num = 196;
|
||||
if ((500 / 42) + num > 200) {
|
||||
bar = param;
|
||||
} else {
|
||||
bar = "This_should_never_happen";
|
||||
}
|
||||
|
||||
String cmd = "echo ";
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process p = r.exec(cmd + bar);
|
||||
}
|
||||
}
|
||||
29
tests/fixtures/real_world/java/taint/cmdi_processbuilder_command.expect.json
vendored
Normal file
29
tests/fixtures/real_world/java/taint/cmdi_processbuilder_command.expect.json
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"description": "HttpServletRequest parameter flows through a List into ProcessBuilder.command(argList) — command injection via the setter form (list attached separately from the constructor, then pb.start()). This is the dominant OWASP Benchmark cmdi shape; resolved via type-qualified ProcessBuilder.command sink on the typed receiver plus container-element taint on the argument list.",
|
||||
"tags": [
|
||||
"taint",
|
||||
"cmdi",
|
||||
"servlet",
|
||||
"container"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"strict_unexpected": [
|
||||
"taint-unsanitised-flow"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "HIGH",
|
||||
"must_match": true,
|
||||
"line_range": [
|
||||
16,
|
||||
16
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "request.getParameter (line 8) is concatenated into a list element (argList.add at line 13), the list is attached to ProcessBuilder via pb.command(argList) at line 16, and executed by pb.start() at line 17. The type-qualified ProcessBuilder.command sink fires at line 16 on the tainted container argument. Exactly one finding survives.",
|
||||
"max_count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
19
tests/fixtures/real_world/java/taint/cmdi_processbuilder_command.java
vendored
Normal file
19
tests/fixtures/real_world/java/taint/cmdi_processbuilder_command.java
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import java.io.*;
|
||||
import java.util.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
public class ProcessCommandHandler extends HttpServlet {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String param = request.getParameter("vector");
|
||||
|
||||
List<String> argList = new ArrayList<String>();
|
||||
argList.add("sh");
|
||||
argList.add("-c");
|
||||
argList.add("echo " + param);
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder();
|
||||
pb.command(argList);
|
||||
pb.start();
|
||||
}
|
||||
}
|
||||
30
tests/fixtures/real_world/java/taint/cmdi_runtime_split_receiver.expect.json
vendored
Normal file
30
tests/fixtures/real_world/java/taint/cmdi_runtime_split_receiver.expect.json
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"description": "HttpServletRequest header flows into a String[] env array passed to a split-receiver Runtime.exec — command injection via the `Runtime r = Runtime.getRuntime(); ... r.exec(cmd, argsEnv)` shape (the dominant remaining OWASP Benchmark cmdi form). The callee text at the sink is `r.exec`, which does not suffix-match the flat `Runtime.exec` rule; resolution depends on the receiver `r` carrying TypeKind::Runtime (from the `Runtime.getRuntime()` factory / the `Runtime` declared type) so the type-qualified resolver rewrites `r.exec` → `Runtime.exec`. Taint is in the env array (arg 1), so no payload-arg restriction may be applied.",
|
||||
"tags": [
|
||||
"taint",
|
||||
"cmdi",
|
||||
"servlet",
|
||||
"runtime",
|
||||
"split-receiver"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"strict_unexpected": [
|
||||
"taint-unsanitised-flow"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "HIGH",
|
||||
"must_match": true,
|
||||
"line_range": [
|
||||
16,
|
||||
16
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "request.getHeader (line 7) flows into the env array element argsEnv (line 15), which is passed as arg 1 of r.exec at line 16. The receiver r is typed Runtime via Runtime.getRuntime() (line 13), so the type-qualified Runtime.exec sink fires at the split-receiver call. Exactly one finding survives.",
|
||||
"max_count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
18
tests/fixtures/real_world/java/taint/cmdi_runtime_split_receiver.java
vendored
Normal file
18
tests/fixtures/real_world/java/taint/cmdi_runtime_split_receiver.java
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import java.io.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
public class RuntimeSplitReceiverHandler extends HttpServlet {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String param = request.getHeader("vector");
|
||||
|
||||
// Split-receiver Runtime.exec: the receiver is bound to a local in
|
||||
// one statement, then exec is called on it in another. The OWASP
|
||||
// Benchmark cmdi shape places the tainted data in the environment
|
||||
// array (arg 1), not the command (arg 0).
|
||||
Runtime r = Runtime.getRuntime();
|
||||
String[] args = { "/bin/sh", "-c", "echo nyx" };
|
||||
String[] argsEnv = { "TAINT=" + param };
|
||||
r.exec(args, argsEnv);
|
||||
}
|
||||
}
|
||||
19
tests/fixtures/real_world/java/taint/cmdi_ternary_const_safe.expect.json
vendored
Normal file
19
tests/fixtures/real_world/java/taint/cmdi_ternary_const_safe.expect.json
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"description": "Constant-condition ternary (OWASP Benchmark cmdi non-vulnerable shape). `(7*18) + num > 200` with num=106 is 232 > 200 — always true — so `bar` is the constant string and the `: param` arm is statically dead. Extending the ternary-RHS diamond split to Java (src/cfg/mod.rs) routes `bar = cond ? const : param` through a real branch+phi CFG; build_ternary_diamond stamps the CondArith tree so fold_constant_branches prunes the dead tainted arm and neutralises its block, exactly as the if-form does. Result: `r.exec(cmd + bar)` carries no taint. Asserts NO taint finding fires.",
|
||||
"tags": [
|
||||
"taint",
|
||||
"cmdi",
|
||||
"servlet",
|
||||
"runtime",
|
||||
"ternary",
|
||||
"const-fold",
|
||||
"precision"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"strict_unexpected": [
|
||||
"taint-unsanitised-flow"
|
||||
],
|
||||
"expected": []
|
||||
}
|
||||
21
tests/fixtures/real_world/java/taint/cmdi_ternary_const_safe.java
vendored
Normal file
21
tests/fixtures/real_world/java/taint/cmdi_ternary_const_safe.java
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import java.io.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
// Constant-condition ternary (OWASP Benchmark cmdi non-vulnerable shape).
|
||||
// `(7*18) + num` is `126 + 106 = 232 > 200` — ALWAYS true — so `bar` is the
|
||||
// constant string and the `: param` arm is statically dead. Routing the Java
|
||||
// ternary through the branch+phi diamond lets `fold_constant_branches` prune
|
||||
// the dead tainted arm exactly as it does for the if-form — NO finding.
|
||||
public class TernaryConstSafe extends HttpServlet {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String param = request.getHeader("vector");
|
||||
|
||||
int num = 106;
|
||||
String bar = (7 * 18) + num > 200 ? "This_should_always_happen" : param;
|
||||
|
||||
String cmd = "echo ";
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process p = r.exec(cmd + bar);
|
||||
}
|
||||
}
|
||||
32
tests/fixtures/real_world/java/taint/cmdi_ternary_param_vuln.expect.json
vendored
Normal file
32
tests/fixtures/real_world/java/taint/cmdi_ternary_param_vuln.expect.json
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"description": "Constant-condition ternary with VULNERABLE polarity. `(500/42) + num > 200` is `11 + 196 = 207 > 200` (integer division) — always true — and the TRUE arm assigns the tainted `param`, so the reachable arm carries taint and only the `: \"...\"` const arm is dead. The Java ternary diamond split + fold must prune the DEAD const arm while keeping the live `bar = param`, so the command-injection finding at `r.exec(cmd + bar)` MUST still fire. Zero-false-negative guard: proves the diamond/fold never prunes the reachable tainted arm.",
|
||||
"tags": [
|
||||
"taint",
|
||||
"cmdi",
|
||||
"servlet",
|
||||
"runtime",
|
||||
"ternary",
|
||||
"const-fold",
|
||||
"no-false-negative"
|
||||
],
|
||||
"modes": [
|
||||
"full"
|
||||
],
|
||||
"strict_unexpected": [
|
||||
"taint-unsanitised-flow"
|
||||
],
|
||||
"expected": [
|
||||
{
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "HIGH",
|
||||
"must_match": true,
|
||||
"line_range": [
|
||||
19,
|
||||
19
|
||||
],
|
||||
"evidence_contains": [],
|
||||
"notes": "request.getHeader (line 12) flows into bar on the always-taken true arm (line 15), then into r.exec at line 19. Exactly one finding survives.",
|
||||
"max_count": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
21
tests/fixtures/real_world/java/taint/cmdi_ternary_param_vuln.java
vendored
Normal file
21
tests/fixtures/real_world/java/taint/cmdi_ternary_param_vuln.java
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import java.io.*;
|
||||
import javax.servlet.http.*;
|
||||
|
||||
// Constant-condition ternary, VULNERABLE polarity. `(500/42) + num` is
|
||||
// `11 + 196 = 207 > 200` (integer division) — ALWAYS true — and the TRUE arm
|
||||
// selects the tainted `param`, so the reachable arm carries taint and only the
|
||||
// `: "..."` const arm is dead. The fold must prune the dead const arm while
|
||||
// keeping the live `param`, so the cmdi finding at `r.exec` MUST still fire.
|
||||
public class TernaryParamVuln extends HttpServlet {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
String param = request.getHeader("vector");
|
||||
|
||||
int num = 196;
|
||||
String bar = (500 / 42) + num > 200 ? param : "This_should_never_happen";
|
||||
|
||||
String cmd = "echo ";
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process p = r.exec(cmd + bar);
|
||||
}
|
||||
}
|
||||
|
|
@ -19,21 +19,13 @@
|
|||
"evidence_contains": [],
|
||||
"notes": "AST pattern detects executeQuery with string concatenation — SQL injection"
|
||||
},
|
||||
{
|
||||
"rule_id": "java.xss.getwriter_print",
|
||||
"severity": "MEDIUM",
|
||||
"must_match": true,
|
||||
"line_range": [12, 12],
|
||||
"evidence_contains": [],
|
||||
"notes": "response.getWriter().println() with user input — reflected XSS via error response"
|
||||
},
|
||||
{
|
||||
"rule_id": "taint-unsanitised-flow",
|
||||
"severity": "HIGH",
|
||||
"must_match": true,
|
||||
"line_range": [7, 12],
|
||||
"evidence_contains": [],
|
||||
"notes": "request.getParameter flows to response.getWriter().println — user input reflected in error response"
|
||||
"notes": "request.getParameter flows to response.getWriter().println at line 12 — user input reflected in error response. Replaces the retired java.xss.getwriter_print AST pattern: reflected XSS is now a taint sink (Sink(Cap::HTML_ESCAPE)), taint-confirmed rather than flagged on every writer call."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue