SeaWolf-AI commited on
Commit
5535e09
·
verified ·
1 Parent(s): 4c996cf

feat: SARIF exporter for UI

Browse files
Files changed (1) hide show
  1. chitos_sarif.py +122 -0
chitos_sarif.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """치토스 진단 결과 → SARIF 2.1.0 변환기 (Shannon 흡수, 클린룸·OASIS 공개표준).
3
+ 치토스 do_scan/do_deep 결과(dict)를 받아 CI/CD·DevSecOps 호환 SARIF 2.1.0 JSON으로 출력.
4
+ finding 형식: [sev('hi'|'mid'|'low'), [ko,en]title, [ko,en]detail]. good/matrix 등은 결과에 미포함.
5
+ AGPL 코드 미사용 — SARIF는 OASIS 공개표준이며 본 구현은 치토스 자체 코드다."""
6
+ import re
7
+
8
+ _LEVEL = {"hi": "error", "mid": "warning", "low": "note"}
9
+
10
+ # 치토스 finding(영문 제목) 키워드 → OWASP Top 10 2021 · CWE 매핑 (공개표준)
11
+ _MAP = [
12
+ ("security response headers", "A05:2021", "CWE-693"),
13
+ ("hsts", "A02:2021", "CWE-319"),
14
+ ("https", "A02:2021", "CWE-319"),
15
+ ("cookie", "A05:2021", "CWE-1004"),
16
+ ("cors", "A05:2021", "CWE-942"),
17
+ ("csp", "A05:2021", "CWE-693"),
18
+ ("sensitive", "A01:2021", "CWE-538"),
19
+ ("backup", "A05:2021", "CWE-530"),
20
+ ("server version", "A05:2021", "CWE-200"),
21
+ ("x-powered-by", "A05:2021", "CWE-200"),
22
+ ("tech stack", "A05:2021", "CWE-200"),
23
+ ("tls certificate", "A02:2021", "CWE-295"),
24
+ ("spf", "A07:2021", "CWE-290"),
25
+ ("dkim", "A07:2021", "CWE-290"),
26
+ ("dmarc", "A07:2021", "CWE-290"),
27
+ ("dnssec", "A05:2021", "CWE-350"),
28
+ ("caa", "A05:2021", "CWE-295"),
29
+ ("mixed content", "A02:2021", "CWE-311"),
30
+ ("external-script", "A08:2021", "CWE-353"),
31
+ ("sri", "A08:2021", "CWE-353"),
32
+ ("pii", "A01:2021", "CWE-359"),
33
+ ("noindex", "A05:2021", "CWE-16"),
34
+ ]
35
+
36
+
37
+ def _owasp_cwe(en_title):
38
+ t = (en_title or "").lower()
39
+ for kw, owasp, cwe in _MAP:
40
+ if kw in t:
41
+ return owasp, cwe
42
+ return None, None
43
+
44
+
45
+ def _pick(v, li):
46
+ if isinstance(v, (list, tuple)) and len(v) == 2:
47
+ return v[li]
48
+ return str(v)
49
+
50
+
51
+ def _slug(s):
52
+ return (re.sub(r"[^a-zA-Z0-9]+", "-", str(s).lower()).strip("-")[:60]) or "finding"
53
+
54
+
55
+ def to_sarif(d, lang="en"):
56
+ """d = do_scan/do_deep 결과. lang: 'en'(기본, SARIF 권장) | 'ko'."""
57
+ li = 0 if lang == "ko" else 1
58
+ host = d.get("host", "")
59
+ base_uri = "https://%s/" % host if host else ""
60
+ # 오류/차단은 결과 없이 빈 run
61
+ findings = []
62
+ for k in ("sec", "seo", "geo"):
63
+ findings += list(d.get(k, []))
64
+ dp = d.get("deep", {})
65
+ findings += list(dp.get("issues", []))
66
+
67
+ # 비침투(do_scan/do_deep)는 관찰 기반 → proofState=observed.
68
+ # 능동 PoC로 증명된 finding은 호출 측에서 it에 'proven' 표식을 넣어 proofState=proven 처리(향후 능동엔진 연계).
69
+ proof = "proven" if d.get("proven") else "observed"
70
+ rules = {}
71
+ results = []
72
+ for it in findings:
73
+ try:
74
+ sev = it[0]
75
+ title = _pick(it[1], li)
76
+ detail = _pick(it[2], li)
77
+ en_title = _pick(it[1], 1)
78
+ rid = _slug(en_title) # ruleId는 영문 기준으로 언어 무관 고정
79
+ except Exception:
80
+ continue
81
+ lvl = _LEVEL.get(sev, "warning")
82
+ owasp, cwe = _owasp_cwe(en_title)
83
+ if rid not in rules:
84
+ rule = {
85
+ "id": rid,
86
+ "name": title,
87
+ "shortDescription": {"text": title},
88
+ "defaultConfiguration": {"level": lvl},
89
+ "properties": {"tags": [t for t in ["security", owasp, cwe] if t]},
90
+ }
91
+ if owasp or cwe:
92
+ rule["properties"]["owasp"] = owasp
93
+ rule["properties"]["cwe"] = cwe
94
+ rules[rid] = rule
95
+ results.append({
96
+ "ruleId": rid,
97
+ "level": lvl,
98
+ "message": {"text": "%s — %s" % (title, detail)},
99
+ "locations": [{"physicalLocation": {"artifactLocation": {"uri": base_uri or host or "target"}}}],
100
+ "properties": {"proofState": proof, "owasp": owasp, "cwe": cwe},
101
+ })
102
+
103
+ return {
104
+ "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
105
+ "version": "2.1.0",
106
+ "runs": [{
107
+ "tool": {"driver": {
108
+ "name": "CHITOS",
109
+ "informationUri": "https://final-bench-freeweb-security-scan.hf.space",
110
+ "version": "1.1",
111
+ "rules": list(rules.values()),
112
+ }},
113
+ "results": results,
114
+ "properties": {"host": host, "engine": "CHITOS", "nonIntrusive": True, "proofModel": proof},
115
+ }],
116
+ }
117
+
118
+
119
+ if __name__ == "__main__":
120
+ import json, sys
121
+ data = json.load(open(sys.argv[1], encoding="utf-8")) if len(sys.argv) > 1 else {"host": "example.com", "sec": [["mid", ["보안 헤더 누락", "Missing security headers"], ["꺼짐", "disabled"]]]}
122
+ print(json.dumps(to_sarif(data), ensure_ascii=False, indent=1))