@panguard-ai/threat-cloud 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin-dashboard.d.ts +11 -0
- package/dist/admin-dashboard.d.ts.map +1 -0
- package/dist/admin-dashboard.js +482 -0
- package/dist/admin-dashboard.js.map +1 -0
- package/dist/backup.d.ts.map +1 -1
- package/dist/backup.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/database.d.ts +22 -3
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +380 -65
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/llm-reviewer.d.ts.map +1 -1
- package/dist/llm-reviewer.js +1 -3
- package/dist/llm-reviewer.js.map +1 -1
- package/dist/server.d.ts +15 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +239 -18
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +35 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threat Cloud Admin Dashboard
|
|
3
|
+
* 威脅雲管理後台 - 需要 Admin API Key 認證
|
|
4
|
+
*
|
|
5
|
+
* Single-page admin UI served at /admin
|
|
6
|
+
* All data fetched client-side via existing API endpoints.
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/threat-cloud/admin-dashboard
|
|
9
|
+
*/
|
|
10
|
+
export declare function getAdminHTML(): string;
|
|
11
|
+
//# sourceMappingURL=admin-dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-dashboard.d.ts","sourceRoot":"","sources":["../src/admin-dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,wBAAgB,YAAY,IAAI,MAAM,CAudrC"}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Threat Cloud Admin Dashboard
|
|
3
|
+
* 威脅雲管理後台 - 需要 Admin API Key 認證
|
|
4
|
+
*
|
|
5
|
+
* Single-page admin UI served at /admin
|
|
6
|
+
* All data fetched client-side via existing API endpoints.
|
|
7
|
+
*
|
|
8
|
+
* @module @panguard-ai/threat-cloud/admin-dashboard
|
|
9
|
+
*/
|
|
10
|
+
export function getAdminHTML() {
|
|
11
|
+
return `<!DOCTYPE html>
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<head>
|
|
14
|
+
<meta charset="utf-8"/>
|
|
15
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
16
|
+
<meta name="robots" content="noindex,nofollow"/>
|
|
17
|
+
<title>Threat Cloud Admin</title>
|
|
18
|
+
<style>
|
|
19
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
20
|
+
:root{--bg:#0a0e14;--surface:#111820;--surface2:#1a2230;--border:#243040;--text:#e0e6ed;--dim:#6b7d8f;--accent:#4fd1c5;--accent2:#38b2ac;--red:#f56565;--orange:#ed8936;--yellow:#ecc94b;--green:#48bb78;--blue:#4299e1}
|
|
21
|
+
body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;font-size:14px;line-height:1.5}
|
|
22
|
+
a{color:var(--accent);text-decoration:none}
|
|
23
|
+
/* Login */
|
|
24
|
+
#login{display:flex;align-items:center;justify-content:center;height:100vh}
|
|
25
|
+
#login form{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:40px;width:380px;text-align:center}
|
|
26
|
+
#login h1{font-size:20px;margin-bottom:8px}
|
|
27
|
+
#login p{color:var(--dim);font-size:13px;margin-bottom:24px}
|
|
28
|
+
#login input{width:100%;padding:10px 14px;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:14px;margin-bottom:16px}
|
|
29
|
+
#login input:focus{outline:none;border-color:var(--accent)}
|
|
30
|
+
#login button{width:100%;padding:10px;background:var(--accent);color:var(--bg);border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer}
|
|
31
|
+
#login button:hover{background:var(--accent2)}
|
|
32
|
+
#login .error{color:var(--red);font-size:13px;margin-top:8px;display:none}
|
|
33
|
+
/* Layout */
|
|
34
|
+
#app{display:none}
|
|
35
|
+
header{background:var(--surface);border-bottom:1px solid var(--border);padding:12px 24px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:10}
|
|
36
|
+
header h1{font-size:16px;font-weight:600}
|
|
37
|
+
header h1 span{color:var(--accent)}
|
|
38
|
+
header .meta{display:flex;align-items:center;gap:16px;font-size:13px;color:var(--dim)}
|
|
39
|
+
header .logout{color:var(--red);cursor:pointer;font-size:12px}
|
|
40
|
+
nav{background:var(--surface);border-bottom:1px solid var(--border);padding:0 24px;display:flex;gap:0;overflow-x:auto}
|
|
41
|
+
nav button{background:none;border:none;border-bottom:2px solid transparent;color:var(--dim);padding:10px 16px;font-size:13px;cursor:pointer;white-space:nowrap}
|
|
42
|
+
nav button:hover{color:var(--text)}
|
|
43
|
+
nav button.active{color:var(--accent);border-bottom-color:var(--accent)}
|
|
44
|
+
main{padding:24px;max-width:1400px;margin:0 auto}
|
|
45
|
+
/* Cards */
|
|
46
|
+
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;margin-bottom:24px}
|
|
47
|
+
.card{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:16px}
|
|
48
|
+
.card .label{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:4px}
|
|
49
|
+
.card .value{font-size:28px;font-weight:700}
|
|
50
|
+
.card .value.green{color:var(--green)}
|
|
51
|
+
.card .value.blue{color:var(--blue)}
|
|
52
|
+
.card .value.orange{color:var(--orange)}
|
|
53
|
+
.card .value.red{color:var(--red)}
|
|
54
|
+
/* Table */
|
|
55
|
+
.table-wrap{background:var(--surface);border:1px solid var(--border);border-radius:10px;overflow:hidden;margin-bottom:24px}
|
|
56
|
+
.table-header{padding:16px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px}
|
|
57
|
+
.table-header h2{font-size:15px;font-weight:600}
|
|
58
|
+
.table-header .controls{display:flex;gap:8px;flex-wrap:wrap}
|
|
59
|
+
.table-header input,.table-header select{padding:6px 10px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px}
|
|
60
|
+
.table-header input:focus,.table-header select:focus{outline:none;border-color:var(--accent)}
|
|
61
|
+
table{width:100%;border-collapse:collapse}
|
|
62
|
+
th{background:var(--surface2);text-align:left;padding:10px 14px;font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:0.5px;font-weight:600}
|
|
63
|
+
td{padding:10px 14px;border-top:1px solid var(--border);font-size:13px;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
64
|
+
tr:hover td{background:var(--surface2)}
|
|
65
|
+
/* Badges */
|
|
66
|
+
.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:600;text-transform:uppercase}
|
|
67
|
+
.badge.critical{background:rgba(245,101,101,.15);color:var(--red)}
|
|
68
|
+
.badge.high{background:rgba(237,137,54,.15);color:var(--orange)}
|
|
69
|
+
.badge.medium{background:rgba(236,201,75,.15);color:var(--yellow)}
|
|
70
|
+
.badge.low{background:rgba(72,187,120,.15);color:var(--green)}
|
|
71
|
+
.badge.informational{background:rgba(66,153,225,.15);color:var(--blue)}
|
|
72
|
+
.badge.sigma{background:rgba(66,153,225,.15);color:var(--blue)}
|
|
73
|
+
.badge.yara{background:rgba(237,137,54,.15);color:var(--orange)}
|
|
74
|
+
.badge.atr{background:rgba(79,209,197,.15);color:var(--accent)}
|
|
75
|
+
/* Charts */
|
|
76
|
+
.chart-row{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:24px}
|
|
77
|
+
.chart-box{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:16px}
|
|
78
|
+
.chart-box h3{font-size:14px;margin-bottom:12px}
|
|
79
|
+
.bar-chart .bar-item{display:flex;align-items:center;gap:8px;margin-bottom:6px}
|
|
80
|
+
.bar-chart .bar-label{width:140px;font-size:12px;color:var(--dim);text-align:right;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
81
|
+
.bar-chart .bar-track{flex:1;height:20px;background:var(--bg);border-radius:4px;overflow:hidden}
|
|
82
|
+
.bar-chart .bar-fill{height:100%;border-radius:4px;min-width:2px;display:flex;align-items:center;padding-left:6px;font-size:11px;font-weight:600;color:var(--bg)}
|
|
83
|
+
.bar-chart .bar-count{width:50px;font-size:12px;color:var(--dim);text-align:right}
|
|
84
|
+
/* Pagination */
|
|
85
|
+
.pagination{display:flex;justify-content:center;gap:8px;padding:16px}
|
|
86
|
+
.pagination button{padding:6px 12px;background:var(--surface2);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:13px;cursor:pointer}
|
|
87
|
+
.pagination button:hover{border-color:var(--accent)}
|
|
88
|
+
.pagination button:disabled{opacity:.4;cursor:default}
|
|
89
|
+
.pagination span{padding:6px 8px;font-size:13px;color:var(--dim)}
|
|
90
|
+
/* Loading */
|
|
91
|
+
.loading{text-align:center;padding:40px;color:var(--dim)}
|
|
92
|
+
/* Empty */
|
|
93
|
+
.empty{text-align:center;padding:40px;color:var(--dim);font-size:13px}
|
|
94
|
+
/* Responsive */
|
|
95
|
+
@media(max-width:768px){.chart-row{grid-template-columns:1fr}.cards{grid-template-columns:repeat(2,1fr)}}
|
|
96
|
+
</style>
|
|
97
|
+
</head>
|
|
98
|
+
<body>
|
|
99
|
+
|
|
100
|
+
<div id="login">
|
|
101
|
+
<form onsubmit="return doLogin(event)">
|
|
102
|
+
<h1>Threat Cloud <span>Admin</span></h1>
|
|
103
|
+
<p>Enter your admin API key to continue.</p>
|
|
104
|
+
<input type="password" id="keyInput" placeholder="TC_ADMIN_API_KEY" autofocus/>
|
|
105
|
+
<button type="submit">Login</button>
|
|
106
|
+
<div class="error" id="loginError">Invalid API key</div>
|
|
107
|
+
</form>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div id="app">
|
|
111
|
+
<header>
|
|
112
|
+
<h1>Threat Cloud <span>Admin</span></h1>
|
|
113
|
+
<div class="meta">
|
|
114
|
+
<span id="uptimeText"></span>
|
|
115
|
+
<span class="logout" onclick="doLogout()">Logout</span>
|
|
116
|
+
</div>
|
|
117
|
+
</header>
|
|
118
|
+
<nav id="tabs">
|
|
119
|
+
<button class="active" data-tab="overview">Overview</button>
|
|
120
|
+
<button data-tab="rules">Rules</button>
|
|
121
|
+
<button data-tab="threats">Threats</button>
|
|
122
|
+
<button data-tab="proposals">ATR Proposals</button>
|
|
123
|
+
<button data-tab="skills">Skill Threats</button>
|
|
124
|
+
<button data-tab="blacklist">Blacklist</button>
|
|
125
|
+
<button data-tab="feeds">Feeds</button>
|
|
126
|
+
</nav>
|
|
127
|
+
<main id="content">
|
|
128
|
+
<div class="loading">Loading...</div>
|
|
129
|
+
</main>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<script>
|
|
133
|
+
let API_KEY='';
|
|
134
|
+
let stats=null;
|
|
135
|
+
let currentTab='overview';
|
|
136
|
+
|
|
137
|
+
// Auth
|
|
138
|
+
function doLogin(e){
|
|
139
|
+
e.preventDefault();
|
|
140
|
+
const key=document.getElementById('keyInput').value.trim();
|
|
141
|
+
if(!key)return false;
|
|
142
|
+
API_KEY=key;
|
|
143
|
+
fetch('/api/stats',{headers:{Authorization:'Bearer '+key}})
|
|
144
|
+
.then(r=>{if(!r.ok)throw new Error();return r.json()})
|
|
145
|
+
.then(d=>{
|
|
146
|
+
if(!d.ok)throw new Error();
|
|
147
|
+
stats=d.data;
|
|
148
|
+
sessionStorage.setItem('tc_key',key);
|
|
149
|
+
document.getElementById('login').style.display='none';
|
|
150
|
+
document.getElementById('app').style.display='block';
|
|
151
|
+
renderOverview();
|
|
152
|
+
fetchUptime();
|
|
153
|
+
})
|
|
154
|
+
.catch(()=>{
|
|
155
|
+
document.getElementById('loginError').style.display='block';
|
|
156
|
+
});
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
function doLogout(){
|
|
160
|
+
sessionStorage.removeItem('tc_key');
|
|
161
|
+
API_KEY='';
|
|
162
|
+
document.getElementById('app').style.display='none';
|
|
163
|
+
document.getElementById('login').style.display='flex';
|
|
164
|
+
document.getElementById('keyInput').value='';
|
|
165
|
+
document.getElementById('loginError').style.display='none';
|
|
166
|
+
}
|
|
167
|
+
// Auto-login from session
|
|
168
|
+
(function(){
|
|
169
|
+
const k=sessionStorage.getItem('tc_key');
|
|
170
|
+
if(k){document.getElementById('keyInput').value=k;doLogin(new Event('submit'));}
|
|
171
|
+
})();
|
|
172
|
+
|
|
173
|
+
// Tabs
|
|
174
|
+
document.getElementById('tabs').addEventListener('click',e=>{
|
|
175
|
+
if(e.target.tagName!=='BUTTON')return;
|
|
176
|
+
document.querySelectorAll('#tabs button').forEach(b=>b.classList.remove('active'));
|
|
177
|
+
e.target.classList.add('active');
|
|
178
|
+
currentTab=e.target.dataset.tab;
|
|
179
|
+
renderTab(currentTab);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
function renderTab(tab){
|
|
183
|
+
switch(tab){
|
|
184
|
+
case 'overview':renderOverview();break;
|
|
185
|
+
case 'rules':renderRules();break;
|
|
186
|
+
case 'threats':renderThreats();break;
|
|
187
|
+
case 'proposals':renderProposals();break;
|
|
188
|
+
case 'skills':renderSkills();break;
|
|
189
|
+
case 'blacklist':renderBlacklist();break;
|
|
190
|
+
case 'feeds':renderFeeds();break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function api(path){
|
|
195
|
+
return fetch(path,{headers:{Authorization:'Bearer '+API_KEY}}).then(r=>r.json());
|
|
196
|
+
}
|
|
197
|
+
function apiText(path){
|
|
198
|
+
return fetch(path,{headers:{Authorization:'Bearer '+API_KEY}}).then(r=>r.text());
|
|
199
|
+
}
|
|
200
|
+
function $(s){return document.getElementById(s)||document.querySelector(s)}
|
|
201
|
+
function h(s){return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')}
|
|
202
|
+
function num(n){return Number(n).toLocaleString()}
|
|
203
|
+
function badge(text,cls){return '<span class="badge '+cls+'">'+h(text)+'</span>'}
|
|
204
|
+
function severityBadge(s){return badge(s,(s||'').toLowerCase())}
|
|
205
|
+
function sourceBadge(s){return badge(s,(s||'').toLowerCase())}
|
|
206
|
+
function timeAgo(iso){
|
|
207
|
+
if(!iso)return'-';
|
|
208
|
+
const d=new Date(iso),now=Date.now(),diff=now-d.getTime();
|
|
209
|
+
if(diff<60000)return 'just now';
|
|
210
|
+
if(diff<3600000)return Math.floor(diff/60000)+'m ago';
|
|
211
|
+
if(diff<86400000)return Math.floor(diff/3600000)+'h ago';
|
|
212
|
+
return Math.floor(diff/86400000)+'d ago';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function fetchUptime(){
|
|
216
|
+
api('/health').then(d=>{
|
|
217
|
+
if(d.ok){
|
|
218
|
+
const s=Math.round(d.data.uptime);
|
|
219
|
+
const h=Math.floor(s/3600),m=Math.floor((s%3600)/60);
|
|
220
|
+
$('uptimeText').textContent='Uptime: '+h+'h '+m+'m';
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Overview
|
|
226
|
+
function renderOverview(){
|
|
227
|
+
if(!stats){api('/api/stats').then(d=>{stats=d.data;renderOverview();});return;}
|
|
228
|
+
const s=stats;
|
|
229
|
+
const maxCat=Math.max(...s.rulesByCategory.map(c=>c.count));
|
|
230
|
+
const maxSev=Math.max(...s.rulesBySeverity.map(c=>c.count));
|
|
231
|
+
const catColors=['var(--accent)','var(--blue)','var(--orange)','var(--yellow)','var(--green)','var(--red)'];
|
|
232
|
+
|
|
233
|
+
let html='<div class="cards">';
|
|
234
|
+
html+='<div class="card"><div class="label">Total Rules</div><div class="value green">'+num(s.totalRules)+'</div></div>';
|
|
235
|
+
html+='<div class="card"><div class="label">Total Threats</div><div class="value blue">'+num(s.totalThreats)+'</div></div>';
|
|
236
|
+
html+='<div class="card"><div class="label">Last 24h Threats</div><div class="value orange">'+num(s.last24hThreats)+'</div></div>';
|
|
237
|
+
html+='<div class="card"><div class="label">ATR Proposals</div><div class="value">'+num(s.proposalStats.total)+'</div></div>';
|
|
238
|
+
html+='<div class="card"><div class="label">Skill Threats</div><div class="value">'+num(s.skillThreatsTotal)+'</div></div>';
|
|
239
|
+
html+='<div class="card"><div class="label">Blacklisted Skills</div><div class="value red">'+num(s.skillBlacklistTotal)+'</div></div>';
|
|
240
|
+
html+='</div>';
|
|
241
|
+
|
|
242
|
+
// Source breakdown
|
|
243
|
+
html+='<div class="cards" style="margin-bottom:24px">';
|
|
244
|
+
(s.rulesBySource||[]).forEach(r=>{
|
|
245
|
+
html+='<div class="card"><div class="label">'+h(r.source).toUpperCase()+' Rules</div><div class="value">'+num(r.count)+'</div></div>';
|
|
246
|
+
});
|
|
247
|
+
html+='</div>';
|
|
248
|
+
|
|
249
|
+
// Charts
|
|
250
|
+
html+='<div class="chart-row">';
|
|
251
|
+
// Category chart
|
|
252
|
+
html+='<div class="chart-box"><h3>Rules by Category</h3><div class="bar-chart">';
|
|
253
|
+
(s.rulesByCategory||[]).slice(0,12).forEach((c,i)=>{
|
|
254
|
+
const pct=Math.round(c.count/maxCat*100);
|
|
255
|
+
const color=catColors[i%catColors.length];
|
|
256
|
+
html+='<div class="bar-item"><span class="bar-label">'+h(c.category)+'</span><div class="bar-track"><div class="bar-fill" style="width:'+pct+'%;background:'+color+'">'+num(c.count)+'</div></div></div>';
|
|
257
|
+
});
|
|
258
|
+
html+='</div></div>';
|
|
259
|
+
|
|
260
|
+
// Severity chart
|
|
261
|
+
html+='<div class="chart-box"><h3>Rules by Severity</h3><div class="bar-chart">';
|
|
262
|
+
const sevColors={'critical':'var(--red)','high':'var(--orange)','medium':'var(--yellow)','low':'var(--green)','informational':'var(--blue)'};
|
|
263
|
+
(s.rulesBySeverity||[]).forEach(c=>{
|
|
264
|
+
const pct=Math.round(c.count/maxSev*100);
|
|
265
|
+
const color=sevColors[c.severity]||'var(--dim)';
|
|
266
|
+
html+='<div class="bar-item"><span class="bar-label">'+h(c.severity)+'</span><div class="bar-track"><div class="bar-fill" style="width:'+pct+'%;background:'+color+'">'+num(c.count)+'</div></div></div>';
|
|
267
|
+
});
|
|
268
|
+
html+='</div></div>';
|
|
269
|
+
html+='</div>';
|
|
270
|
+
|
|
271
|
+
// MITRE + Attack Types
|
|
272
|
+
html+='<div class="chart-row">';
|
|
273
|
+
html+='<div class="chart-box"><h3>Top Attack Types</h3>';
|
|
274
|
+
if(s.topAttackTypes.length===0)html+='<div class="empty">No threat data yet</div>';
|
|
275
|
+
else{html+='<table><tr><th>Type</th><th>Count</th></tr>';s.topAttackTypes.forEach(t=>{html+='<tr><td>'+h(t.type)+'</td><td>'+num(t.count)+'</td></tr>';});html+='</table>';}
|
|
276
|
+
html+='</div>';
|
|
277
|
+
html+='<div class="chart-box"><h3>Top MITRE Techniques</h3>';
|
|
278
|
+
if(s.topMitreTechniques.length===0)html+='<div class="empty">No threat data yet</div>';
|
|
279
|
+
else{html+='<table><tr><th>Technique</th><th>Count</th></tr>';s.topMitreTechniques.forEach(t=>{html+='<tr><td>'+h(t.technique)+'</td><td>'+num(t.count)+'</td></tr>';});html+='</table>';}
|
|
280
|
+
html+='</div>';
|
|
281
|
+
html+='</div>';
|
|
282
|
+
|
|
283
|
+
$('content').innerHTML=html;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Rules
|
|
287
|
+
let rulesPage=0;const RULES_PER_PAGE=50;let rulesFilter={search:'',source:'',category:'',severity:''};
|
|
288
|
+
function renderRules(){
|
|
289
|
+
$('content').innerHTML='<div class="loading">Loading rules...</div>';
|
|
290
|
+
let url='/api/rules?limit=5000';
|
|
291
|
+
api(url).then(d=>{
|
|
292
|
+
const allRules=d.data||[];
|
|
293
|
+
const filtered=allRules.filter(r=>{
|
|
294
|
+
if(rulesFilter.source&&r.source!==rulesFilter.source)return false;
|
|
295
|
+
if(rulesFilter.category&&r.category!==rulesFilter.category)return false;
|
|
296
|
+
if(rulesFilter.severity&&r.severity!==rulesFilter.severity)return false;
|
|
297
|
+
if(rulesFilter.search){
|
|
298
|
+
const q=rulesFilter.search.toLowerCase();
|
|
299
|
+
return (r.ruleId||'').toLowerCase().includes(q)||(r.ruleContent||'').toLowerCase().includes(q);
|
|
300
|
+
}
|
|
301
|
+
return true;
|
|
302
|
+
});
|
|
303
|
+
const total=filtered.length;
|
|
304
|
+
const pages=Math.ceil(total/RULES_PER_PAGE);
|
|
305
|
+
if(rulesPage>=pages)rulesPage=Math.max(0,pages-1);
|
|
306
|
+
const slice=filtered.slice(rulesPage*RULES_PER_PAGE,(rulesPage+1)*RULES_PER_PAGE);
|
|
307
|
+
|
|
308
|
+
let html='<div class="table-wrap"><div class="table-header"><h2>Rules ('+num(total)+' of '+num(allRules.length)+')</h2>';
|
|
309
|
+
html+='<div class="controls">';
|
|
310
|
+
html+='<input type="text" placeholder="Search..." value="'+h(rulesFilter.search)+'" onchange="rulesFilter.search=this.value;rulesPage=0;renderRules()"/>';
|
|
311
|
+
html+='<select onchange="rulesFilter.source=this.value;rulesPage=0;renderRules()"><option value="">All Sources</option><option value="sigma"'+(rulesFilter.source==='sigma'?' selected':'')+'>Sigma</option><option value="yara"'+(rulesFilter.source==='yara'?' selected':'')+'>YARA</option><option value="atr"'+(rulesFilter.source==='atr'?' selected':'')+'>ATR</option></select>';
|
|
312
|
+
html+='<select onchange="rulesFilter.severity=this.value;rulesPage=0;renderRules()"><option value="">All Severity</option><option value="critical"'+(rulesFilter.severity==='critical'?' selected':'')+'>Critical</option><option value="high"'+(rulesFilter.severity==='high'?' selected':'')+'>High</option><option value="medium"'+(rulesFilter.severity==='medium'?' selected':'')+'>Medium</option><option value="low"'+(rulesFilter.severity==='low'?' selected':'')+'>Low</option></select>';
|
|
313
|
+
html+='</div></div>';
|
|
314
|
+
html+='<table><tr><th>Rule ID</th><th>Source</th><th>Category</th><th>Severity</th><th>Published</th></tr>';
|
|
315
|
+
slice.forEach(r=>{
|
|
316
|
+
html+='<tr><td title="'+h(r.ruleId)+'">'+h((r.ruleId||'').slice(0,60))+'</td>';
|
|
317
|
+
html+='<td>'+sourceBadge(r.source||'unknown')+'</td>';
|
|
318
|
+
html+='<td>'+h(r.category||'unknown')+'</td>';
|
|
319
|
+
html+='<td>'+severityBadge(r.severity||'unknown')+'</td>';
|
|
320
|
+
html+='<td>'+timeAgo(r.publishedAt)+'</td></tr>';
|
|
321
|
+
});
|
|
322
|
+
html+='</table>';
|
|
323
|
+
html+='<div class="pagination">';
|
|
324
|
+
html+='<button onclick="rulesPage=0;renderRules()" '+(rulesPage===0?'disabled':'')+'>First</button>';
|
|
325
|
+
html+='<button onclick="rulesPage--;renderRules()" '+(rulesPage===0?'disabled':'')+'>Prev</button>';
|
|
326
|
+
html+='<span>Page '+(rulesPage+1)+' of '+Math.max(1,pages)+'</span>';
|
|
327
|
+
html+='<button onclick="rulesPage++;renderRules()" '+(rulesPage>=pages-1?'disabled':'')+'>Next</button>';
|
|
328
|
+
html+='<button onclick="rulesPage='+(pages-1)+';renderRules()" '+(rulesPage>=pages-1?'disabled':'')+'>Last</button>';
|
|
329
|
+
html+='</div></div>';
|
|
330
|
+
$('content').innerHTML=html;
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Threats (via stats - no direct GET /api/threats endpoint for list)
|
|
335
|
+
function renderThreats(){
|
|
336
|
+
api('/api/stats').then(d=>{
|
|
337
|
+
const s=d.data;
|
|
338
|
+
let html='<div class="cards">';
|
|
339
|
+
html+='<div class="card"><div class="label">Total Threats</div><div class="value blue">'+num(s.totalThreats)+'</div></div>';
|
|
340
|
+
html+='<div class="card"><div class="label">Last 24h</div><div class="value orange">'+num(s.last24hThreats)+'</div></div>';
|
|
341
|
+
html+='</div>';
|
|
342
|
+
html+='<div class="chart-row">';
|
|
343
|
+
html+='<div class="chart-box"><h3>Attack Types</h3>';
|
|
344
|
+
if(!s.topAttackTypes.length)html+='<div class="empty">No threat data collected yet. Threats are submitted by Guard instances.</div>';
|
|
345
|
+
else{html+='<table><tr><th>Type</th><th>Count</th></tr>';s.topAttackTypes.forEach(t=>{html+='<tr><td>'+h(t.type)+'</td><td>'+num(t.count)+'</td></tr>';});html+='</table>';}
|
|
346
|
+
html+='</div>';
|
|
347
|
+
html+='<div class="chart-box"><h3>MITRE Techniques</h3>';
|
|
348
|
+
if(!s.topMitreTechniques.length)html+='<div class="empty">No MITRE technique data yet.</div>';
|
|
349
|
+
else{html+='<table><tr><th>Technique</th><th>Count</th></tr>';s.topMitreTechniques.forEach(t=>{html+='<tr><td><a href="https://attack.mitre.org/techniques/'+h(t.technique).replace('.','/')+'" target="_blank">'+h(t.technique)+'</a></td><td>'+num(t.count)+'</td></tr>';});html+='</table>';}
|
|
350
|
+
html+='</div></div>';
|
|
351
|
+
$('content').innerHTML=html;
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ATR Proposals
|
|
356
|
+
function renderProposals(){
|
|
357
|
+
$('content').innerHTML='<div class="loading">Loading proposals...</div>';
|
|
358
|
+
api('/api/atr-proposals').then(d=>{
|
|
359
|
+
const proposals=d.data||[];
|
|
360
|
+
let html='<div class="table-wrap"><div class="table-header"><h2>ATR Proposals ('+proposals.length+')</h2></div>';
|
|
361
|
+
if(!proposals.length){html+='<div class="empty">No ATR proposals submitted yet. Proposals are auto-generated when Guard detects new threat patterns.</div>';}
|
|
362
|
+
else{
|
|
363
|
+
html+='<table><tr><th>Pattern Hash</th><th>Status</th><th>Confirmations</th><th>LLM Verdict</th><th>Submitted</th></tr>';
|
|
364
|
+
proposals.forEach(p=>{
|
|
365
|
+
const status=p.status||p.llm_verdict||'pending';
|
|
366
|
+
const cls=status==='approved'?'low':status==='rejected'?'critical':'medium';
|
|
367
|
+
html+='<tr><td title="'+h(p.pattern_hash)+'">'+h((p.pattern_hash||'').slice(0,16))+'...</td>';
|
|
368
|
+
html+='<td>'+badge(status,cls)+'</td>';
|
|
369
|
+
html+='<td>'+num(p.confirmation_count||0)+'</td>';
|
|
370
|
+
html+='<td>'+(p.llm_verdict?badge(p.llm_verdict,p.llm_verdict==='approve'?'low':'critical'):'<span style="color:var(--dim)">pending</span>')+'</td>';
|
|
371
|
+
html+='<td>'+timeAgo(p.created_at)+'</td></tr>';
|
|
372
|
+
});
|
|
373
|
+
html+='</table>';
|
|
374
|
+
}
|
|
375
|
+
html+='</div>';
|
|
376
|
+
$('content').innerHTML=html;
|
|
377
|
+
}).catch(()=>{
|
|
378
|
+
$('content').innerHTML='<div class="empty">Cannot load proposals. Admin auth may be required for this endpoint.</div>';
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Skill Threats
|
|
383
|
+
function renderSkills(){
|
|
384
|
+
$('content').innerHTML='<div class="loading">Loading skill threats...</div>';
|
|
385
|
+
api('/api/skill-threats?limit=200').then(d=>{
|
|
386
|
+
const threats=d.data||[];
|
|
387
|
+
let html='<div class="table-wrap"><div class="table-header"><h2>Skill Threats ('+threats.length+')</h2></div>';
|
|
388
|
+
if(!threats.length){html+='<div class="empty">No skill threats reported yet. Skill threats are submitted when Guard audits a new MCP skill installation.</div>';}
|
|
389
|
+
else{
|
|
390
|
+
html+='<table><tr><th>Skill Name</th><th>Risk Score</th><th>Risk Level</th><th>Skill Hash</th><th>Reported</th></tr>';
|
|
391
|
+
threats.forEach(t=>{
|
|
392
|
+
const cls=(t.risk_level||'').toLowerCase();
|
|
393
|
+
html+='<tr><td>'+h(t.skill_name)+'</td>';
|
|
394
|
+
html+='<td>'+num(t.risk_score)+'</td>';
|
|
395
|
+
html+='<td>'+severityBadge(t.risk_level)+'</td>';
|
|
396
|
+
html+='<td title="'+h(t.skill_hash)+'">'+h((t.skill_hash||'').slice(0,12))+'...</td>';
|
|
397
|
+
html+='<td>'+timeAgo(t.created_at)+'</td></tr>';
|
|
398
|
+
});
|
|
399
|
+
html+='</table>';
|
|
400
|
+
}
|
|
401
|
+
html+='</div>';
|
|
402
|
+
$('content').innerHTML=html;
|
|
403
|
+
}).catch(()=>{
|
|
404
|
+
$('content').innerHTML='<div class="empty">Cannot load skill threats. Admin auth required.</div>';
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Blacklist
|
|
409
|
+
function renderBlacklist(){
|
|
410
|
+
$('content').innerHTML='<div class="loading">Loading blacklist...</div>';
|
|
411
|
+
api('/api/skill-blacklist').then(d=>{
|
|
412
|
+
const list=d.data||[];
|
|
413
|
+
let html='<div class="table-wrap"><div class="table-header"><h2>Community Blacklist ('+list.length+')</h2></div>';
|
|
414
|
+
if(!list.length){html+='<div class="empty">No blacklisted skills yet. Skills are blacklisted when 3+ distinct clients report avg risk >= 70.</div>';}
|
|
415
|
+
else{
|
|
416
|
+
html+='<table><tr><th>Skill Name</th><th>Avg Risk</th><th>Max Level</th><th>Reports</th><th>First Seen</th><th>Last Seen</th></tr>';
|
|
417
|
+
list.forEach(s=>{
|
|
418
|
+
html+='<tr><td>'+h(s.skillName)+'</td>';
|
|
419
|
+
html+='<td>'+num(s.avgRiskScore)+'</td>';
|
|
420
|
+
html+='<td>'+severityBadge(s.maxRiskLevel)+'</td>';
|
|
421
|
+
html+='<td>'+num(s.reportCount)+'</td>';
|
|
422
|
+
html+='<td>'+timeAgo(s.firstReported)+'</td>';
|
|
423
|
+
html+='<td>'+timeAgo(s.lastReported)+'</td></tr>';
|
|
424
|
+
});
|
|
425
|
+
html+='</table>';
|
|
426
|
+
}
|
|
427
|
+
html+='</div>';
|
|
428
|
+
$('content').innerHTML=html;
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Feeds
|
|
433
|
+
function renderFeeds(){
|
|
434
|
+
Promise.all([
|
|
435
|
+
apiText('/api/feeds/ip-blocklist'),
|
|
436
|
+
apiText('/api/feeds/domain-blocklist'),
|
|
437
|
+
api('/api/skill-whitelist')
|
|
438
|
+
]).then(([ips,domains,wl])=>{
|
|
439
|
+
const ipList=(ips||'').trim().split('\\n').filter(Boolean);
|
|
440
|
+
const domList=(domains||'').trim().split('\\n').filter(Boolean);
|
|
441
|
+
const whiteList=(wl.data||[]);
|
|
442
|
+
|
|
443
|
+
let html='<div class="cards">';
|
|
444
|
+
html+='<div class="card"><div class="label">IP Blocklist</div><div class="value red">'+num(ipList.length)+'</div></div>';
|
|
445
|
+
html+='<div class="card"><div class="label">Domain Blocklist</div><div class="value orange">'+num(domList.length)+'</div></div>';
|
|
446
|
+
html+='<div class="card"><div class="label">Skill Whitelist</div><div class="value green">'+num(whiteList.length)+'</div></div>';
|
|
447
|
+
html+='</div>';
|
|
448
|
+
|
|
449
|
+
html+='<div class="chart-row">';
|
|
450
|
+
// IP Blocklist
|
|
451
|
+
html+='<div class="chart-box"><h3>IP Blocklist</h3>';
|
|
452
|
+
if(!ipList.length)html+='<div class="empty">No blocked IPs yet.</div>';
|
|
453
|
+
else{html+='<table><tr><th>IP Address</th></tr>';ipList.slice(0,50).forEach(ip=>{html+='<tr><td>'+h(ip)+'</td></tr>';});if(ipList.length>50)html+='<tr><td style="color:var(--dim)">...and '+(ipList.length-50)+' more</td></tr>';html+='</table>';}
|
|
454
|
+
html+='</div>';
|
|
455
|
+
// Domain Blocklist
|
|
456
|
+
html+='<div class="chart-box"><h3>Domain Blocklist</h3>';
|
|
457
|
+
if(!domList.length)html+='<div class="empty">No blocked domains yet.</div>';
|
|
458
|
+
else{html+='<table><tr><th>Domain</th></tr>';domList.slice(0,50).forEach(d=>{html+='<tr><td>'+h(d)+'</td></tr>';});if(domList.length>50)html+='<tr><td style="color:var(--dim)">...and '+(domList.length-50)+' more</td></tr>';html+='</table>';}
|
|
459
|
+
html+='</div></div>';
|
|
460
|
+
|
|
461
|
+
// Skill Whitelist
|
|
462
|
+
html+='<div class="table-wrap"><div class="table-header"><h2>Community Skill Whitelist ('+whiteList.length+')</h2></div>';
|
|
463
|
+
if(!whiteList.length){html+='<div class="empty">No whitelisted skills yet.</div>';}
|
|
464
|
+
else{
|
|
465
|
+
html+='<table><tr><th>Skill Name</th><th>Reports</th><th>Fingerprint</th></tr>';
|
|
466
|
+
whiteList.forEach(s=>{
|
|
467
|
+
html+='<tr><td>'+h(s.skill_name||s.skillName)+'</td>';
|
|
468
|
+
html+='<td>'+num(s.report_count||s.reportCount||0)+'</td>';
|
|
469
|
+
html+='<td title="'+h(s.fingerprint_hash||'')+'">'+h((s.fingerprint_hash||'-').slice(0,16))+'</td></tr>';
|
|
470
|
+
});
|
|
471
|
+
html+='</table>';
|
|
472
|
+
}
|
|
473
|
+
html+='</div>';
|
|
474
|
+
|
|
475
|
+
$('content').innerHTML=html;
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
</script>
|
|
479
|
+
</body>
|
|
480
|
+
</html>`;
|
|
481
|
+
}
|
|
482
|
+
//# sourceMappingURL=admin-dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-dashboard.js","sourceRoot":"","sources":["../src/admin-dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,UAAU,YAAY;IAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAqdD,CAAC;AACT,CAAC"}
|
package/dist/backup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,SAAI;IAkB7D;;;;;;;OAOG;IACH,MAAM,IAAI,YAAY;IA0CtB;;OAEG;IACH,MAAM,IAAI,IAAI;IAsBd;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,SAAI;IAkB7D;;;;;;;OAOG;IACH,MAAM,IAAI,YAAY;IA0CtB;;OAEG;IACH,MAAM,IAAI,IAAI;IAsBd;;OAEG;IACH,WAAW,IAAI,MAAM,EAAE;IAevB;;OAEG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAKzC"}
|
package/dist/backup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../src/backup.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -54,9 +54,9 @@ async function main() {
|
|
|
54
54
|
const config = {
|
|
55
55
|
port: args.port ?? Number(process.env['PORT'] ?? '8080'),
|
|
56
56
|
host: args.host ?? '0.0.0.0',
|
|
57
|
-
dbPath: args.dbPath ?? process.env['DB_PATH'] ?? './threat-cloud.db',
|
|
57
|
+
dbPath: args.dbPath ?? process.env['TC_DB_PATH'] ?? process.env['DB_PATH'] ?? './threat-cloud.db',
|
|
58
58
|
apiKeyRequired: args.apiKeyRequired ?? false,
|
|
59
|
-
apiKeys: args.apiKeys ?? [],
|
|
59
|
+
apiKeys: args.apiKeys ?? process.env['TC_API_KEYS']?.split(',') ?? [],
|
|
60
60
|
rateLimitPerMinute: 120,
|
|
61
61
|
anthropicApiKey: args.anthropicApiKey ?? process.env['ANTHROPIC_API_KEY'],
|
|
62
62
|
adminApiKey: args.adminApiKey ?? process.env['TC_ADMIN_API_KEY'],
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,MAAM,GAA0B,EAAE,CAAC;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,KAAK,QAAQ;gBACX,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAChC,MAAM;YACR,KAAK,QAAQ;gBACX,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxB,MAAM;YACR,KAAK,MAAM;gBACT,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC1B,MAAM;YACR,KAAK,WAAW;gBACd,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC;gBAC7B,MAAM,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC9C,MAAM;YACR,KAAK,qBAAqB;gBACxB,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBACnC,MAAM;YACR,KAAK,iBAAiB;gBACpB,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC/B,MAAM;YACR,KAAK,QAAQ;gBACX,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;CAanB,CAAC,CAAC;gBACK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAiB;QAC3B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;QACxD,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;QAC5B,MAAM,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/database.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @panguard-ai/threat-cloud/database
|
|
8
8
|
*/
|
|
9
|
-
import type { AnonymizedThreatData, ThreatCloudRule, ThreatStats, ATRProposal, SkillThreatSubmission } from './types.js';
|
|
9
|
+
import type { AnonymizedThreatData, ThreatCloudRule, ThreatStats, ATRProposal, SkillThreatSubmission, SkillBlacklistEntry } from './types.js';
|
|
10
10
|
/**
|
|
11
11
|
* Threat Cloud database backed by SQLite
|
|
12
12
|
* 基於 SQLite 的威脅雲資料庫
|
|
@@ -16,14 +16,26 @@ export declare class ThreatCloudDB {
|
|
|
16
16
|
constructor(dbPath: string);
|
|
17
17
|
/** Create tables if they don't exist / 建立資料表 */
|
|
18
18
|
private initialize;
|
|
19
|
+
/** Run schema migrations for existing databases / 執行既有資料庫的 schema 遷移 */
|
|
20
|
+
private migrate;
|
|
19
21
|
/** Insert anonymized threat data / 插入匿名化威脅數據 */
|
|
20
22
|
insertThreat(data: AnonymizedThreatData): void;
|
|
23
|
+
/** Extract classification metadata from rule content / 從規則內容提取分類元資料 */
|
|
24
|
+
private extractMetadata;
|
|
21
25
|
/** Insert or update a community rule / 插入或更新社群規則 */
|
|
22
26
|
upsertRule(rule: ThreatCloudRule): void;
|
|
23
27
|
/** Fetch rules published after a given timestamp / 取得指定時間後發佈的規則 */
|
|
24
|
-
getRulesSince(since: string
|
|
28
|
+
getRulesSince(since: string, filters?: {
|
|
29
|
+
category?: string;
|
|
30
|
+
severity?: string;
|
|
31
|
+
source?: string;
|
|
32
|
+
}): ThreatCloudRule[];
|
|
25
33
|
/** Fetch all rules with limit / 取得所有規則(含限制) */
|
|
26
|
-
getAllRules(limit?: number
|
|
34
|
+
getAllRules(limit?: number, filters?: {
|
|
35
|
+
category?: string;
|
|
36
|
+
severity?: string;
|
|
37
|
+
source?: string;
|
|
38
|
+
}): ThreatCloudRule[];
|
|
27
39
|
/** Insert ATR rule proposal / 插入 ATR 規則提案 */
|
|
28
40
|
insertATRProposal(proposal: ATRProposal): void;
|
|
29
41
|
/** Get ATR proposals, optionally filtered by status / 取得 ATR 提案 */
|
|
@@ -79,6 +91,13 @@ export declare class ThreatCloudDB {
|
|
|
79
91
|
hash: string | null;
|
|
80
92
|
confirmations: number;
|
|
81
93
|
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Get skill blacklist: skills reported by 3+ distinct clients with avg risk >= 70
|
|
96
|
+
* 取得技能黑名單:3+ 不同客戶端回報且平均風險 >= 70 的技能
|
|
97
|
+
*/
|
|
98
|
+
getSkillBlacklist(minReports?: number, minAvgRisk?: number): SkillBlacklistEntry[];
|
|
99
|
+
/** Backfill classification for rules with NULL category / 回填缺少分類的規則 */
|
|
100
|
+
backfillClassification(): number;
|
|
82
101
|
/** Close the database / 關閉資料庫 */
|
|
83
102
|
close(): void;
|
|
84
103
|
}
|
package/dist/database.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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"}
|