Ferr0 commited on
Commit
3255f2b
·
verified ·
1 Parent(s): 2236bd7

repo-audit: detection via HF Inference (Qwen3-Coder-480B), 7B keeps refutation

Browse files
Files changed (3) hide show
  1. README.md +5 -4
  2. app.py +8 -7
  3. engine.py +17 -14
README.md CHANGED
@@ -42,10 +42,11 @@ input): only the real one survives, with its PoC.
42
  - **Whole-repo, split by model size, both via HF Inference** — detection on
43
  [Qwen3-Coder-480B-A35B-Instruct](https://huggingface.co/Qwen/Qwen3-Coder-480B-A35B-Instruct): a
44
  whole-file view follows a long source→sink data-flow (an injection can be ~200 lines from its
45
- sink) the small model misses. Refutation on a small model (Qwen2.5-Coder-7B) over a **tight,
46
- line-numbered window** around each candidate a focused context is what lets it confirm a real
47
- finding and drop false positives (a whole file misleads it: it excuses an LFI on a `file_exists`
48
- guard). Both stages off-GPU, so a whole-repo scan has no length limit.
 
49
 
50
  Paste any snippet, pick the language, audit. All example code is fictional and **intentionally
51
  vulnerable** — it's the test set. This is a **defensive / educational** demo, not a production scanner.
 
42
  - **Whole-repo, split by model size, both via HF Inference** — detection on
43
  [Qwen3-Coder-480B-A35B-Instruct](https://huggingface.co/Qwen/Qwen3-Coder-480B-A35B-Instruct): a
44
  whole-file view follows a long source→sink data-flow (an injection can be ~200 lines from its
45
+ sink) the small model misses. Refutation on **Qwen2.5-Coder-32B** over a **bounded, line-numbered
46
+ window** around each candidate: it must *credit* real sanitization (prepared statements, allow-lists,
47
+ casts) to drop false positives a 7B refuter can't and confirms almost everything while not being
48
+ fooled by `file_exists`/`isset` guards (which don't stop an LFI). Both stages off-GPU, so a whole-repo
49
+ scan has no length limit.
50
 
51
  Paste any snippet, pick the language, audit. All example code is fictional and **intentionally
52
  vulnerable** — it's the test set. This is a **defensive / educational** demo, not a production scanner.
app.py CHANGED
@@ -38,19 +38,20 @@ MAX_FINDINGS = int(os.environ.get("MAX_FINDINGS", "5"))
38
  # The repo audit runs BOTH stages on HF Inference by default:
39
  # - detection on a LARGE model (480B): whole-file detection needs a wide view + a long data-flow
40
  # (source→sink can be ~200 lines apart, e.g. an LFI through an object), which the 7B misses.
41
- # - refutation on a SMALL model (7B) over a TIGHT window: a focused context is what lets it confirm
42
- # a real finding (a whole file misleads it it excuses an LFI on a nearby file_exists guard).
 
43
  # Both off-GPU ⇒ a long whole-repo scan needs NO ZeroGPU reservation, so there is no proxy-token to
44
  # expire mid-scan (the limit that capped multi-file scans). ZeroGPU is the no-token fallback.
45
  DETECT_BACKEND = os.environ.get("DETECT_BACKEND", "hf_inference").strip().lower()
46
  REFUTE_BACKEND = os.environ.get("REFUTE_BACKEND", "hf_inference").strip().lower()
47
  HF_INFERENCE_MODEL = os.environ.get("HF_INFERENCE_MODEL", "Qwen/Qwen3-Coder-480B-A35B-Instruct") # detect (large)
48
- HF_REFUTE_MODEL = os.environ.get("HF_REFUTE_MODEL", "Qwen/Qwen2.5-Coder-7B-Instruct") # refute (small, targeted)
49
  HF_TOKEN = os.environ.get("HF_TOKEN")
50
  # Rough $/1M-token figure for cost *logging* only (the real cost shows on the HF billing dashboard).
51
  USD_PER_MTOK = float(os.environ.get("HF_INFERENCE_USD_PER_MTOK", "0.9"))
52
  REFUTE_CHUNK = int(os.environ.get("REFUTE_CHUNK", "6")) # candidates per ZeroGPU call (zerogpu fallback only)
53
- REFUTE_CTX = int(os.environ.get("REFUTE_CTX", "10")) # ± lines of targeted context for refutation (keep small)
54
 
55
  _inf_client = InferenceClient(token=HF_TOKEN) if HF_TOKEN else None
56
  if _inf_client is None: # no token → both repo stages fall back to the local 7B on ZeroGPU
@@ -237,8 +238,8 @@ Audit a **whole public repository**. It clones shallow (or unpacks your `.zip`)
237
  sandbox, ranks the code files **by security relevance**, and scans the top ones **file-by-file with
238
  streaming** — two stages **split by model size**: detection runs on a large model
239
  (**Qwen3-Coder-480B**) that can follow a long source→sink data-flow across a whole file, then each
240
- candidate is **adversarially refuted by a small model (Qwen2.5-Coder-7B) on a tight context** so
241
- false positives are dropped — both via **HF Inference**.
242
  **Static analysis only — the target code is never executed. Public / authorized repos only.**
243
 
244
  ⏱️ A scan uses HF Inference credits (both stages, off-GPU — no scan-length limit). Try
@@ -248,7 +249,7 @@ false positives are dropped — both via **HF Inference**.
248
  ABOUT = """
249
  ---
250
  Snippet: **Qwen2.5-Coder-7B** on **ZeroGPU** · whole-repo: detect **Qwen3-Coder-480B** + refute
251
- **Qwen2.5-Coder-7B**, both via **HF Inference** · static analysis, no code execution, no secrets · built by
252
  [Ferr0](https://huggingface.co/Ferr0) · [pixelium.win](https://pixelium.win) · [GitHub](https://github.com/ferr079)
253
  """
254
 
 
38
  # The repo audit runs BOTH stages on HF Inference by default:
39
  # - detection on a LARGE model (480B): whole-file detection needs a wide view + a long data-flow
40
  # (source→sink can be ~200 lines apart, e.g. an LFI through an object), which the 7B misses.
41
+ # - refutation on a CAPABLE model (32B) over a bounded window (±REFUTE_CTX): the refuter must CREDIT
42
+ # real sanitization (prepared statements, allow-lists, casts)a 7B refuter can't, so it drowns the
43
+ # report in false positives — while not being fooled by file_exists/isset guards (see REFUTE_SYS).
44
  # Both off-GPU ⇒ a long whole-repo scan needs NO ZeroGPU reservation, so there is no proxy-token to
45
  # expire mid-scan (the limit that capped multi-file scans). ZeroGPU is the no-token fallback.
46
  DETECT_BACKEND = os.environ.get("DETECT_BACKEND", "hf_inference").strip().lower()
47
  REFUTE_BACKEND = os.environ.get("REFUTE_BACKEND", "hf_inference").strip().lower()
48
  HF_INFERENCE_MODEL = os.environ.get("HF_INFERENCE_MODEL", "Qwen/Qwen3-Coder-480B-A35B-Instruct") # detect (large)
49
+ HF_REFUTE_MODEL = os.environ.get("HF_REFUTE_MODEL", "Qwen/Qwen2.5-Coder-32B-Instruct") # refute (judge — needs to credit real sanitization)
50
  HF_TOKEN = os.environ.get("HF_TOKEN")
51
  # Rough $/1M-token figure for cost *logging* only (the real cost shows on the HF billing dashboard).
52
  USD_PER_MTOK = float(os.environ.get("HF_INFERENCE_USD_PER_MTOK", "0.9"))
53
  REFUTE_CHUNK = int(os.environ.get("REFUTE_CHUNK", "6")) # candidates per ZeroGPU call (zerogpu fallback only)
54
+ REFUTE_CTX = int(os.environ.get("REFUTE_CTX", "60")) # ± lines of context for refutation (enough to see the local sanitization, not the whole file)
55
 
56
  _inf_client = InferenceClient(token=HF_TOKEN) if HF_TOKEN else None
57
  if _inf_client is None: # no token → both repo stages fall back to the local 7B on ZeroGPU
 
238
  sandbox, ranks the code files **by security relevance**, and scans the top ones **file-by-file with
239
  streaming** — two stages **split by model size**: detection runs on a large model
240
  (**Qwen3-Coder-480B**) that can follow a long source→sink data-flow across a whole file, then each
241
+ candidate is **adversarially refuted by Qwen2.5-Coder-32B on a bounded context** — it credits real
242
+ sanitization (prepared statements, allow-lists) and drops false positives — both via **HF Inference**.
243
  **Static analysis only — the target code is never executed. Public / authorized repos only.**
244
 
245
  ⏱️ A scan uses HF Inference credits (both stages, off-GPU — no scan-length limit). Try
 
249
  ABOUT = """
250
  ---
251
  Snippet: **Qwen2.5-Coder-7B** on **ZeroGPU** · whole-repo: detect **Qwen3-Coder-480B** + refute
252
+ **Qwen2.5-Coder-32B**, both via **HF Inference** · static analysis, no code execution, no secrets · built by
253
  [Ferr0](https://huggingface.co/Ferr0) · [pixelium.win](https://pixelium.win) · [GitHub](https://github.com/ferr079)
254
  """
255
 
engine.py CHANGED
@@ -17,20 +17,23 @@ DETECT_SYS = (
17
  "ignore pure style, quality or error-handling nits. Use 1-based line numbers."
18
  )
19
  REFUTE_SYS = (
20
- "You are an exploit analyst verifying ONE claimed vulnerability in the given code. Decide "
21
- "whether it is GENUINELY exploitable.\n"
22
- "- EXPLOITABLE (exploitable=true): untrusted input reaches a dangerous sink without adequate "
23
- "validation/sanitization, AND you can give a concrete proof-of-concept input. Example: a "
24
- "shell command built by concatenating an unsanitized parameter is exploitable.\n"
25
- "- FALSE POSITIVE (exploitable=false): the attacker-controlled input is constrained to a SAFE "
26
- "set before the sink (cast to int, allow-listed, properly escaped or parameterized) or the "
27
- "sink is unreachable (dead code).\n"
28
- "A check that does NOT restrict the attacker-controlled VALUE is NOT adequate, do not be "
29
- "fooled by it: e.g. file_exists()/is_file() before include/require still allows path traversal "
30
- "to any existing file (LFI), and an isset/empty/length/non-empty check does not stop "
31
- "injection. Such guards do NOT make a finding a false positive.\n"
32
- "Be rigorous BOTH ways: do not confirm vague suspicions, but do NOT dismiss a real, "
33
- "reachable, unsanitized sink. If exploitable, set exploitable=true and give the PoC input."
 
 
 
34
  )
35
 
36
  SEV = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "⚪"}
 
17
  "ignore pure style, quality or error-handling nits. Use 1-based line numbers."
18
  )
19
  REFUTE_SYS = (
20
+ "You are an exploit analyst verifying ONE claimed vulnerability in the given code. Decide if it "
21
+ "is GENUINELY exploitable. Judge ONLY what the code shows.\n"
22
+ "- EXPLOITABLE (exploitable=true): attacker-controlled input reaches the dangerous sink with NO "
23
+ "adequate sanitization in between; give a concrete proof-of-concept input.\n"
24
+ "- FALSE POSITIVE (exploitable=false): adequate protection on the value before the sink — a "
25
+ "prepared/parameterized query, an allow-list of permitted values, a cast to a safe type, proper "
26
+ "escaping (escapeshellarg, htmlspecialchars), or strict value validation (is_numeric on the whole "
27
+ "value). Also FP if the sink is unreachable/dead code, or the input is not attacker-controlled. "
28
+ "If a proper sanitization like that is visible, it IS a false positive say so.\n"
29
+ "Two traps, do NOT fall for them:\n"
30
+ "1. An EXISTENCE/presence check is NOT sanitization. If attacker input is used to build a path "
31
+ "passed to include/require/file_get_contents/fopen/readfile EVEN with a fixed directory prefix, "
32
+ "a forced extension (.php), or a file_exists()/is_file() guard it IS EXPLOITABLE (path traversal "
33
+ "/ LFI): the attacker still controls which file loads, and such prefix/suffix/existence constraints "
34
+ "are routinely bypassed (../, encoded traversal, existing sensitive files).\n"
35
+ "2. isset/empty/strlen do not stop injection.\n"
36
+ "If exploitable, set exploitable=true and give the PoC input."
37
  )
38
 
39
  SEV = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "⚪"}