Views
No views yet
cisco-ai/SecureBERT2.0 backbone, this model employs a Multi-Task Learning (MTL) architecture:LayerNorm -> Linear -> GELU -> Dropout -> Linear -> Softmax) mapped to the [CLS] token.LayerNorm -> Dropout -> Linear -> GELU -> Dropout -> Linear -> GELU -> Dropout -> Linear -> L2 Normalization) mapped to the Mean-Pooled token embeddings. These heads project the text into a spherical vector space of 768 dimensions.config.json: Contains the cvss_map defining the output tensor sizes and label decoders for the CVSS classification heads.cwe_embeddings_new.pkl: A serialized dictionary containing the pre-computed, L2-normalized 768-d vectors for all reference CWE definitions. This file is required for the CWE retrieval process.1import json
2import pickle
3import torch
4import torch.nn as nn
5import torch.nn.functional as F
6from transformers import AutoConfig, AutoModel, AutoTokenizer
7from huggingface_hub import hf_hub_download
8
9# 1. Define the Custom Multi-Head Architecture
10class SecureBERTMultiHead(nn.Module):
11 def __init__(self, model_name_or_path):
12 super().__init__()
13 self.modernbert_config = AutoConfig.from_pretrained(model_name_or_path)
14 if hasattr(self.modernbert_config, "reference_compile"):
15 self.modernbert_config.reference_compile = False
16
17 self.bert = AutoModel.from_config(self.modernbert_config)
18 cvss_map = getattr(self.modernbert_config, "cvss_map", {})
19
20 self.cvss_heads = nn.ModuleDict({
21 k: nn.Sequential(
22 nn.LayerNorm(768), nn.Dropout(0.1),
23 nn.Linear(768, 768), nn.GELU(), nn.Dropout(0.1),
24 nn.Linear(768, 768), nn.GELU(), nn.Dropout(0.1),
25 nn.Linear(768, len(classes)), nn.Softmax(dim=1)
26 ) for k, classes in cvss_map.items()
27 })
28
29 self.cwe_heads = nn.ModuleDict({
30 k: nn.Sequential(
31 nn.LayerNorm(768), nn.Dropout(0.1),
32 nn.Linear(768, 768), nn.GELU(), nn.Dropout(0.1),
33 nn.Linear(768, 768), nn.GELU(), nn.Dropout(0.1),
34 nn.Linear(768, 768)
35 ) for k in['pillar', 'class', 'base', 'variant']
36 })
37
38 def forward(self, input_ids, attention_mask):
39 out = self.bert(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state
40 cls_emb = out[:, 0, :]
41
42 mask = attention_mask.unsqueeze(-1).expand(out.size()).float()
43 mean_emb = torch.sum(out * mask, 1) / torch.clamp(mask.sum(1), min=1e-9)
44
45 res = {}
46 for k, head in self.cvss_heads.items():
47 res[k] = head(cls_emb)
48
49 for k, head in self.cwe_heads.items():
50 res[k] = F.normalize(head(mean_emb), p=2, dim=1)
51
52 return res
53
54# 2. Inference Wrapper
55class VulnRetriever:
56 def __init__(self, repo_id):
57 self.device = "cuda" if torch.cuda.is_available() else "cpu"
58
59 conf_path = hf_hub_download(repo_id=repo_id, filename="config.json")
60 model_path = hf_hub_download(repo_id=repo_id, filename="pytorch_model.bin")
61 emb_path = hf_hub_download(repo_id=repo_id, filename="cwe_embeddings_new.pkl")
62
63 with open(conf_path, "r", encoding='utf-8') as f:
64 self.config = json.load(f)
65
66 self.cvss_map = self.config.get("cvss_map", {})
67 base_model_name = self.config.get("base_model", "cisco-ai/SecureBERT2.0-biencoder")
68
69 self.tokenizer = AutoTokenizer.from_pretrained(base_model_name)
70 self.model = SecureBERTMultiHead(repo_id)
71
72 self.model.load_state_dict(torch.load(model_path, map_location=self.device), strict=False)
73 self.model.to(self.device).eval()
74
75 with open(emb_path, 'rb') as f:
76 self.embeddings_map = pickle.load(f)
77
78 self.candidates = {}
79 for level, data in self.embeddings_map.items():
80 ids = list(data.keys())
81 vecs = torch.stack([data[k]['vector'] for k in ids]).to(self.device)
82 vecs = F.normalize(vecs, p=2, dim=1)
83 self.candidates[level] = {'ids': ids, 'matrix': vecs}
84
85 def predict(self, text, top_k=3):
86 inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512, padding=True).to(self.device)
87
88 with torch.no_grad():
89 out = self.model(inputs['input_ids'], inputs['attention_mask'])
90
91 results = {'cvss': {}, 'cwe': {}}
92
93 for task, labels in self.cvss_map.items():
94 score, idx = torch.max(out[task], dim=1)
95 idx_val = idx.item()
96 results['cvss'][task] = {
97 'value': labels[idx_val] if idx_val < len(labels) else "Unknown",
98 'confidence': round(score.item(), 4)
99 }
100
101 for level in ['pillar', 'class', 'base', 'variant']:
102 if level not in self.candidates: continue
103
104 query_vec = out[level]
105 cand_matrix = self.candidates[level]['matrix']
106 cand_ids = self.candidates[level]['ids']
107
108 scores = torch.matmul(query_vec, cand_matrix.T).squeeze()
109 if scores.dim() == 0: scores = scores.unsqueeze(0)
110
111 top_scores, top_indices = torch.topk(scores, k=min(top_k, scores.size(0)))
112 probs = F.softmax(top_scores, dim=0)
113
114 level_preds =[]
115 for score_val, idx in zip(probs, top_indices):
116 cwe_id = cand_ids[idx.item()]
117 cwe_name = self.embeddings_map[level][cwe_id]['name']
118 level_preds.append({
119 'id': int(str(cwe_id).replace('CWE-', '')),
120 'name': cwe_name,
121 'score': round(score_val.item(), 4)
122 })
123
124 results['cwe'][level] = level_preds
125
126 return results
127
128# 3. Execution
129if __name__ == "__main__":
130 REPO_ID = "YourUsername/Your-Triplet-Repo-Name"
131
132 retriever = VulnRetriever(REPO_ID)
133
134 sample_cve = "A buffer overflow in the web server allows remote attackers to execute arbitrary code via a long URL."
135 results = retriever.predict(sample_cve)
136
137 print(json.dumps(results, indent=2))