Download app/analysis_modules.py from wuhp/bbscanner: direct link, hf CLI and curl.
- Browser
- Download file 6.76 kB
-
https://huggingface.co/spaces/wuhp/bbscanner/resolve/main/app/analysis_modules.py
- Command line
-
hf download hf://spaces/wuhp/bbscanner/app/analysis_modules.py
-
curl -L -o analysis_modules.py https://huggingface.co/spaces/wuhp/bbscanner/resolve/main/app/analysis_modules.py
6.76 kB
| """Keyless native WAF fingerprinting and supplied-source SAST adapters.""" | |
| import json | |
| import tempfile | |
| from pathlib import Path | |
| from .module_inputs import ModuleInputs | |
| from .pipeline import execute | |
| from .web_modules import alert, read_report | |
| SAST_RULES = { | |
| "asfb-js-postmessage-wildcard": "Review wildcard postMessage target origin and message sensitivity.", | |
| "asfb-python-shell-true": "Review subprocess execution with shell=True.", | |
| "asfb-python-dynamic-code": "Review dynamically evaluated code and input provenance.", | |
| "asfb-python-pickle": "Deserialization must only accept trusted input.", | |
| "asfb-python-unsafe-yaml": "Review use of an unsafe YAML loader.", | |
| "asfb-python-tls-disabled": "Review disabled certificate verification.", | |
| "asfb-js-eval": "Review dynamic evaluation and input provenance.", | |
| "asfb-js-function-constructor": "Review dynamic Function construction.", | |
| "asfb-js-shell-command": "Review shell command execution and input provenance.", | |
| "asfb-js-html-assignment": "Review HTML assignments for untrusted content.", | |
| } | |
| LANGUAGES = {"python": ".py", "javascript": ".js", "typescript": ".ts"} | |
| def normalized_sast(report, target): | |
| findings = [] | |
| seen = set() | |
| for item in report.get("results", []): | |
| if not isinstance(item, dict): | |
| continue | |
| rule = str(item.get("check_id", "")).rsplit(".", 1)[-1] | |
| line = (item.get("start") or {}).get("line") | |
| if rule not in SAST_RULES or type(line) is not int or not 1 <= line <= 200001: | |
| continue | |
| if (rule, line) in seen: | |
| continue | |
| seen.add((rule, line)) | |
| finding = alert("semgrep", "Semgrep: " + SAST_RULES[rule], target, | |
| f"Supplied source, line {line}; rule {rule}. " | |
| "Pattern review only; input trust, reachability and exploitability are not proven. " | |
| "Source, values and native context are redacted.", "Medium", rule) | |
| finding["source_line"] = line | |
| findings.append(finding) | |
| return findings[:100] | |
| async def run(module, url, host, agent, delay, inputs=None): | |
| values = inputs if isinstance(inputs, ModuleInputs) else ModuleInputs(**(inputs or {})) | |
| if module == "semgrep" and (not values.source_text.strip() or values.sast_language not in LANGUAGES): | |
| reason = "Requires supplied source text and an explicit Python, JavaScript or TypeScript language" | |
| return {"source": module, "status": "skipped", "alerts": [], "reason": reason, "output": reason} | |
| with tempfile.TemporaryDirectory(prefix="asfb-" + module + "-") as tmp: | |
| root = Path(tmp) | |
| output = root / "report.json" | |
| if module == "semgrep": | |
| source = root / ("source" + LANGUAGES[values.sast_language]) | |
| source.write_text(values.source_text, encoding="utf-8") | |
| source.chmod(0o600) | |
| args = ["/usr/bin/env", "SEMGREP_SEND_METRICS=off", "SEMGREP_ENABLE_VERSION_CHECK=0", | |
| "SEMGREP_APP_TOKEN=", "/opt/semgrep/bin/semgrep", "scan", "--oss-only", | |
| "--config", "/app/app/semgrep-rules/review.yaml", "--json", "--output", str(output), | |
| "--metrics", "off", "--disable-version-check", "--no-git-ignore", | |
| "--jobs", "1", "--timeout", "5", "--max-memory", "512", | |
| "--max-target-bytes", "1000000", str(source)] | |
| deadline = 75 | |
| reason = "Supplied source only; ten bundled pattern-review rules; CE single-file analysis; no code execution, remote registry, uploads or autofix" | |
| elif module == "wafw00f": | |
| spec = root / "spec.json" | |
| spec.write_text(json.dumps({"target": url, "host": host, "agent": agent, "delay": delay}), encoding="utf-8") | |
| args = ["/opt/wafw00f/bin/python", "/app/app/waf_runner.py", str(spec), str(output)] | |
| deadline = 45 | |
| reason = "Native WAFW00F vendor signatures; up to eight paced same-origin GET requests; no redirects or generic blocking heuristics" | |
| else: | |
| raise ValueError("Unsupported analysis module") | |
| rows, status = await execute(args, deadline) | |
| report = read_report(output) | |
| findings, vendors, requests = [], [], 0 | |
| if module == "semgrep" and isinstance(report, dict) and isinstance(report.get("results"), list): | |
| findings = normalized_sast(report, url) | |
| errors = report.get("errors") or [] | |
| scanned = (report.get("paths") or {}).get("scanned") or [] | |
| if errors and status == "completed": | |
| status = "partial" if findings else "failed" | |
| reason += "; native analysis errors were reported (details redacted)" | |
| if not scanned and status == "completed": | |
| status = "failed" | |
| reason += "; supplied file was not reported as scanned" | |
| elif module == "wafw00f" and isinstance(report, dict) and report.get("profile") == "wafw00f-bounded": | |
| vendors = [name for name in report.get("vendors", []) if isinstance(name, str) and len(name) <= 120][:10] | |
| requests = report.get("requests", 0) | |
| requests = requests if type(requests) is int and 0 <= requests <= 8 else 0 | |
| if report.get("status") in ("partial", "failed"): | |
| status = report["status"] if status == "completed" else status | |
| for vendor in vendors: | |
| findings.append(alert(module, "WAF fingerprint: " + vendor, url, | |
| "Native vendor-signature observation; this is infrastructure inventory, not a vulnerability. " | |
| "Header/body signatures can be spoofed or hidden. Raw responses and probe URLs are omitted.")) | |
| if report.get("stop_reason") in ("request_budget", "response_limit", "rate_limited", "deadline", | |
| "request_failed", "scope_blocked", "probe_blocked"): | |
| reason += "; " + report["stop_reason"].replace("_", " ") | |
| else: | |
| if status == "completed": | |
| status = "failed" | |
| reason += "; no valid native report" | |
| if status == "completed" and findings: | |
| status = "findings" | |
| if status in ("timeout", "failed", "unavailable"): | |
| reason += "; incomplete scan; parsed results retained" | |
| return {"source": module, "target": url, "status": status, "alerts": findings, | |
| "waf": {"vendors": vendors, "requests": requests} if module == "wafw00f" else None, | |
| "requests": requests, "deadline_seconds": deadline, "reason": reason, | |
| "output": f"{module}: {len(findings)} observations; status: {status}; native output redacted"} | |