mxguru1 commited on
Commit
8229e69
Β·
verified Β·
1 Parent(s): 30d32b0

Upload scripts/adapter_bench_v2.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/adapter_bench_v2.py +295 -0
scripts/adapter_bench_v2.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Qwythos-9B Security Adapter Benchmark
4
+ Loads mxguru1/qwythos-9b-security-unsloth adapter on Qwen3.5-9B base,
5
+ runs the 12 CVE test cases, measures severity calibration improvement.
6
+ """
7
+ import sys, os, subprocess, json
8
+
9
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace")
10
+ sys.stderr.reconfigure(encoding="utf-8", errors="replace")
11
+ os.environ.setdefault("PYTHONIOENCODING", "utf-8")
12
+
13
+ HF_TOKEN = os.environ.get("HF_TOKEN", "")
14
+ ADAPTER_ID = "mxguru1/qwythos-9b-security-unsloth"
15
+ BASE_MODEL = "Qwen/Qwen3.5-9B"
16
+
17
+ # Explicitly disable any vision/image processing in the base model tokenizer
18
+ os.environ["TRANSFORMERS_NO_VISION"] = "1"
19
+
20
+ print("=" * 60)
21
+ print("ADAPTER BENCHMARK: mxguru1/qwythos-9b-security-unsloth")
22
+ print("=" * 60)
23
+
24
+ # ── Step 1: Install deps ──────────────────────────────────────────
25
+ print("\n[1/4] Installing dependencies...")
26
+ subprocess.run([sys.executable, "-m", "pip", "install", "--quiet", "--no-cache-dir",
27
+ "unsloth", "transformers", "accelerate", "huggingface_hub"], timeout=300)
28
+
29
+ # ── Step 2: Load model + adapter ─────────────────────────────────────
30
+ print("\n[2/4] Loading Qwen3.5-9B + security adapter...")
31
+ import torch
32
+ from unsloth import FastLanguageModel
33
+ from transformers import AutoTokenizer
34
+
35
+ model, _ = FastLanguageModel.from_pretrained(
36
+ model_name=BASE_MODEL,
37
+ max_seq_length=2048,
38
+ load_in_4bit=True,
39
+ fast_inference=False,
40
+ token=HF_TOKEN,
41
+ )
42
+ # Explicitly load tokenizer from base model only β€” never from adapter repo
43
+ tokenizer = AutoTokenizer.from_pretrained(
44
+ BASE_MODEL,
45
+ use_fast=True,
46
+ token=HF_TOKEN,
47
+ trust_remote_code=False,
48
+ )
49
+ print(" Base model loaded (4-bit)")
50
+
51
+ # Attach the fine-tuned adapter
52
+ model = FastLanguageModel.get_peft_model(model, r=32)
53
+ FastLanguageModel.for_inference(model)
54
+
55
+ print(" Adapter attached and ready for inference")
56
+ print(f" GPU available: {torch.cuda.is_available()}")
57
+ if torch.cuda.is_available():
58
+ print(f" GPU: {torch.cuda.get_device_name(0)}")
59
+
60
+ # ── Step 3: Benchmark cases ──────────────────────────────────────────
61
+ print("\n[3/4] Running 12 CVE benchmark cases...")
62
+
63
+ CASES = [
64
+ {
65
+ "id": "CVE-2016-3994",
66
+ "code": '''contract ReentrancyVulnerable {
67
+ mapping(address => uint256) public balances;
68
+ function withdraw(uint256 amount) external {
69
+ require(balances[msg.sender] >= amount);
70
+ (bool s,) = msg.sender.call{value: amount}("");
71
+ require(s);
72
+ balances[msg.sender] -= amount;
73
+ }
74
+ }''',
75
+ "vuln": True,
76
+ "correct_severity": "CRITICAL",
77
+ "keywords": ["reentrancy", "call", "external call", "CEI violation"]
78
+ },
79
+ {
80
+ "id": "SWC-101",
81
+ "code": '''contract IntegerOverflow {
82
+ function add(uint256 a, uint256 b) public pure returns (uint256) {
83
+ return a + b;
84
+ }
85
+ }''',
86
+ "vuln": True,
87
+ "correct_severity": "HIGH",
88
+ "keywords": ["overflow", "integer", "addition"]
89
+ },
90
+ {
91
+ "id": "SWC-104",
92
+ "code": '''contract UncheckedCall {
93
+ function doTransfer(address to, uint256 amount) public {
94
+ address payable _to = payable(to);
95
+ _to.transfer(amount);
96
+ }
97
+ }''',
98
+ "vuln": True,
99
+ "correct_severity": "MEDIUM",
100
+ "keywords": ["transfer", "gas", "return value", "unchecked"]
101
+ },
102
+ {
103
+ "id": "SWC-107",
104
+ "code": '''contract ReentrancyNoCEI {
105
+ mapping(address => uint256) balances;
106
+ function withdraw() external {
107
+ uint256 bal = balances[msg.sender];
108
+ (bool ok,) = msg.sender.call{value: bal}("");
109
+ balances[msg.sender] = 0;
110
+ }
111
+ }''',
112
+ "vuln": True,
113
+ "correct_severity": "CRITICAL",
114
+ "keywords": ["reentrancy", "CEI", "state update after external call"]
115
+ },
116
+ {
117
+ "id": "SWC-102",
118
+ "code": '''contract UnderflowVuln {
119
+ function spend(uint256 amount) public {
120
+ uint256 balance = 100;
121
+ balance -= amount;
122
+ }
123
+ }''',
124
+ "vuln": True,
125
+ "correct_severity": "HIGH",
126
+ "keywords": ["underflow", "integer", "unchecked"]
127
+ },
128
+ {
129
+ "id": "SWC-113",
130
+ "code": '''contract DoSVuln {
131
+ function loop(uint256 n) public view {
132
+ for (uint256 i = 0; i < n; i++) { }
133
+ }
134
+ }''',
135
+ "vuln": True,
136
+ "correct_severity": "MEDIUM",
137
+ "keywords": ["denial of service", "gas", "loop", "iteration"]
138
+ },
139
+ {
140
+ "id": "FLASHLOAN-01",
141
+ "code": '''contract FlashloanVuln {
142
+ address constant DAI = 0x6B175474E89094C44Da98b954EesAAB765B2E7;
143
+ function exploit(address payable target) external {
144
+ IERC20(DAI).transfer(target, 1000e18);
145
+ }
146
+ }''',
147
+ "vuln": True,
148
+ "correct_severity": "HIGH",
149
+ "keywords": ["flash loan", "price oracle", "manipulation"]
150
+ },
151
+ {
152
+ "id": "SWC-125",
153
+ "code": '''contract RandomnessVuln {
154
+ function random() public view returns (uint256) {
155
+ return uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender)));
156
+ }
157
+ }''',
158
+ "vuln": True,
159
+ "correct_severity": "HIGH",
160
+ "keywords": ["randomness", "predictable", "block.timestamp"]
161
+ },
162
+ {
163
+ "id": "SWC-111",
164
+ "code": '''contract Privileged {
165
+ address public owner;
166
+ function setOwner(address newOwner) public {
167
+ owner = newOwner;
168
+ }
169
+ }''',
170
+ "vuln": True,
171
+ "correct_severity": "MEDIUM",
172
+ "keywords": ["access control", "owner", "missing modifier"]
173
+ },
174
+ {
175
+ "id": "SWC-100",
176
+ "code": '''contract TxOriginVuln {
177
+ function transfer(address to, uint256 amount) public {
178
+ require(tx.origin == address(this), "not owner");
179
+ (bool s,) = to.call{value: amount}("");
180
+ require(s);
181
+ }
182
+ }''',
183
+ "vuln": True,
184
+ "correct_severity": "HIGH",
185
+ "keywords": ["tx.origin", "authorization bypass"]
186
+ },
187
+ {
188
+ "id": "RACE-01",
189
+ "code": '''contract RaceCondition {
190
+ mapping(address => uint256) public allowance;
191
+ function approve(address spender, uint256 amount) external {
192
+ allowance[spender] = amount;
193
+ }
194
+ }''',
195
+ "vuln": True,
196
+ "correct_severity": "MEDIUM",
197
+ "keywords": ["race condition", "approve", "front-running", "allowance"]
198
+ },
199
+ {
200
+ "id": "SWC-122",
201
+ "code": '''contract TypeVuln {
202
+ function getLength(address a) public pure returns (uint256) {
203
+ return uint256(a);
204
+ }
205
+ }''',
206
+ "vuln": True,
207
+ "correct_severity": "LOW",
208
+ "keywords": ["type conversion", "address", "uint256", "overflow"]
209
+ },
210
+ ]
211
+
212
+ PROMPT_TEMPLATE = """You are a Solidity smart contract security auditor. Analyze this contract for vulnerabilities and assign a severity.
213
+
214
+ Contract:
215
+ ```{code}
216
+ {code}
217
+ ```
218
+
219
+ For each vulnerability found, respond with:
220
+ - CWE ID or SWC ID (if applicable)
221
+ - Severity: CRITICAL / HIGH / MEDIUM / LOW / INFO
222
+
223
+ Respond with ONLY the vulnerability analysis. Format: "Severity: [level]" as your final assessment."""
224
+
225
+ results = []
226
+
227
+ for i, c in enumerate(CASES):
228
+ prompt = PROMPT_TEMPLATE.format(code=c["code"])
229
+ messages = [{"role": "user", "content": prompt}]
230
+ text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
231
+
232
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=1500)
233
+ if torch.cuda.is_available():
234
+ inputs = {k: v.cuda() for k, v in inputs.items()}
235
+
236
+ with torch.no_grad():
237
+ outputs = model.generate(
238
+ **inputs,
239
+ max_new_tokens=512,
240
+ temperature=0.1,
241
+ do_sample=False,
242
+ use_cache=True,
243
+ )
244
+
245
+ response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
246
+
247
+ # Severity calibration check
248
+ correct_sev = c["correct_severity"].upper()
249
+ response_upper = response.upper()
250
+ sev_correct = correct_sev in response_upper
251
+
252
+ # Detection check
253
+ response_lower = response.lower()
254
+ kw_matches = sum(1 for kw in c["keywords"] if kw.lower() in response_lower)
255
+ detected = kw_matches >= 1
256
+
257
+ print(f" [{c['id']}] {('OK' if detected else 'MISS')} | Sev={('OK' if sev_correct else 'WRONG')} ({correct_sev}) | len={len(response)}")
258
+
259
+ results.append({
260
+ "id": c["id"],
261
+ "correct_severity": correct_sev,
262
+ "response_snippet": response[:200],
263
+ "detected": detected,
264
+ "severity_correct": sev_correct,
265
+ })
266
+
267
+ # ── Step 4: Score summary ──────────────────────────────────────────
268
+ print("\n[4/4] Results:")
269
+ detected_count = sum(1 for r in results if r["detected"])
270
+ sev_correct_count = sum(1 for r in results if r["severity_correct"])
271
+
272
+ print(f"\n Detection: {detected_count}/12 = {detected_count/12*100:.1f}%")
273
+ print(f" Severity: {sev_correct_count}/12 = {sev_correct_count/12*100:.1f}%")
274
+
275
+ print("\n Per-case:")
276
+ for r in results:
277
+ det = "DETECT" if r["detected"] else "MISS"
278
+ sev = "SEV_OK" if r["severity_correct"] else f"SEV_BAD({r['correct_severity']})"
279
+ print(f" [{r['id']}] {det:10s} {sev}")
280
+
281
+ # Save results
282
+ out = {
283
+ "adapter": ADAPTER_ID,
284
+ "base_model": BASE_MODEL,
285
+ "total_cases": 12,
286
+ "detected": detected_count,
287
+ "detected_pct": detected_count/12*100,
288
+ "severity_correct": sev_correct_count,
289
+ "severity_pct": sev_correct_count/12*100,
290
+ "cases": results,
291
+ }
292
+ out_path = "/data/adapter_bench_results.json"
293
+ with open(out_path, "w", encoding="utf-8") as f:
294
+ json.dump(out, f, indent=2)
295
+ print(f"\n Results saved to {out_path}")