fullcourtdefense-cli 1.7.12 → 1.7.13
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/commands/local-scan.js +127 -17
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -1096,7 +1096,7 @@ function compactScanHtml(args) {
|
|
|
1096
1096
|
h1 { margin: 6px 0 8px; font-size: 26px; line-height: 1.15; }
|
|
1097
1097
|
p { color: #475569; line-height: 1.55; }
|
|
1098
1098
|
.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
|
|
1099
|
-
.menuShell { display: grid; grid-template-columns: minmax(
|
|
1099
|
+
.menuShell { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.15fr) minmax(180px, .7fr); gap: 14px; margin-top: 18px; align-items: stretch; }
|
|
1100
1100
|
.menuGroup { border: 1px solid #dbeafe; background: rgba(255,255,255,.7); border-radius: 20px; padding: 12px; }
|
|
1101
1101
|
.menuGroup.run { border-color: #bbf7d0; background: #f0fdf4; }
|
|
1102
1102
|
.menuLabel { color: #475569; font-size: 11px; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; margin: 0 0 9px; }
|
|
@@ -1108,6 +1108,11 @@ function compactScanHtml(args) {
|
|
|
1108
1108
|
.sectionTitle h2 { margin: 0; font-size: 18px; }
|
|
1109
1109
|
.muted { color: #64748b; font-size: 13px; }
|
|
1110
1110
|
.card { cursor: pointer; border: 2px solid #e2e8f0; background: white; border-radius: 18px; padding: 16px; text-align: left; transition: .15s ease; }
|
|
1111
|
+
.workflowCard { min-height: 118px; }
|
|
1112
|
+
.workflowCard b { font-size: 18px; }
|
|
1113
|
+
.workflowCard span { font-size: 13px; }
|
|
1114
|
+
.scanTypeGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 12px 0; }
|
|
1115
|
+
.advancedScanGrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin: 8px 0 12px; }
|
|
1111
1116
|
.card:hover, .card.active { border-color: #2563eb; background: #eff6ff; transform: translateY(-1px); }
|
|
1112
1117
|
.card b { display: block; font-size: 14px; margin-bottom: 4px; }
|
|
1113
1118
|
.card span { color: #64748b; font-size: 12px; line-height: 1.35; }
|
|
@@ -1157,8 +1162,8 @@ function compactScanHtml(args) {
|
|
|
1157
1162
|
<main class="wrap">
|
|
1158
1163
|
<section class="hero">
|
|
1159
1164
|
<div class="eyebrow">Local Web Console</div>
|
|
1160
|
-
<h1>
|
|
1161
|
-
<p>
|
|
1165
|
+
<h1>Choose a local workflow</h1>
|
|
1166
|
+
<p>Pick one clear path. Run MCP Tool is a one-off tool call. Security Scan is a red-team scan against an internal endpoint, RAG target, or MCP server.</p>
|
|
1162
1167
|
<div class="auth">
|
|
1163
1168
|
<div class="eyebrow">${isConfigured ? 'Authenticated' : 'Authentication Required'}</div>
|
|
1164
1169
|
<p id="authStatus">${isConfigured
|
|
@@ -1189,19 +1194,15 @@ function compactScanHtml(args) {
|
|
|
1189
1194
|
<div class="menuShell" id="cards">
|
|
1190
1195
|
<div class="menuGroup run">
|
|
1191
1196
|
<div class="menuLabel">Run, not a scan</div>
|
|
1192
|
-
<button class="card active" data-
|
|
1197
|
+
<button class="card workflowCard active" data-workflow="runner"><b>Run one MCP tool</b><span>Call exactly one local MCP tool through a direct server or protected gateway. Best for demos and policy checks.</span></button>
|
|
1193
1198
|
</div>
|
|
1194
1199
|
<div class="menuGroup">
|
|
1195
1200
|
<div class="menuLabel">Security scans</div>
|
|
1196
|
-
<
|
|
1197
|
-
<button class="card" data-target="endpoint"><b>Endpoint Scan</b><span>Red-team an internal agent/chat API.</span></button>
|
|
1198
|
-
<button class="card" data-target="rag"><b>RAG Scan</b><span>Test retrieval URL or local corpus path.</span></button>
|
|
1199
|
-
<button class="card" data-target="mcp"><b>MCP Scan</b><span>Scan tool responses and tool-abuse paths.</span></button>
|
|
1200
|
-
</div>
|
|
1201
|
+
<button class="card workflowCard" data-workflow="scan"><b>Start security scan</b><span>Run quick or full red-team tests against Endpoint, RAG, or MCP targets. Reports save to Full Court Defense.</span></button>
|
|
1201
1202
|
</div>
|
|
1202
1203
|
<div class="menuGroup">
|
|
1203
1204
|
<div class="menuLabel">Help</div>
|
|
1204
|
-
<button class="card" data-
|
|
1205
|
+
<button class="card workflowCard" data-workflow="help"><b>Options</b><span>Modes, formats, and when to use each local workflow.</span></button>
|
|
1205
1206
|
</div>
|
|
1206
1207
|
</div>
|
|
1207
1208
|
</section>
|
|
@@ -1265,6 +1266,19 @@ function compactScanHtml(args) {
|
|
|
1265
1266
|
</div>
|
|
1266
1267
|
</div>
|
|
1267
1268
|
|
|
1269
|
+
<div id="scan-picker" class="hidden">
|
|
1270
|
+
<div class="note">Security scans run red-team templates locally from this machine. First choose what you want to test.</div>
|
|
1271
|
+
<div class="scanTypeGrid">
|
|
1272
|
+
<button class="card scanTypeCard active" data-target="endpoint"><b>Bot Scan</b><span>Red-team an internal bot, agent, or chat API with prompt-injection templates.</span></button>
|
|
1273
|
+
<button class="card scanTypeCard" data-target="posture"><b>Security Posture Scan</b><span>Run the web-style checkup for HTTPS, headers, auth exposure, and endpoint posture.</span></button>
|
|
1274
|
+
</div>
|
|
1275
|
+
<div class="menuLabel">Advanced local targets</div>
|
|
1276
|
+
<div class="advancedScanGrid">
|
|
1277
|
+
<button class="card scanTypeCard" data-target="rag"><b>RAG Scan</b><span>Test retrieval URL or local corpus path.</span></button>
|
|
1278
|
+
<button class="card scanTypeCard" data-target="mcp"><b>MCP Scan</b><span>Scan tool responses and tool-abuse paths.</span></button>
|
|
1279
|
+
</div>
|
|
1280
|
+
</div>
|
|
1281
|
+
|
|
1268
1282
|
<div id="endpoint-fields" class="hidden">
|
|
1269
1283
|
<label>Internal endpoint URL</label>
|
|
1270
1284
|
<input id="endpoint" placeholder="http://127.0.0.1:3000/chat" />
|
|
@@ -1274,6 +1288,30 @@ function compactScanHtml(args) {
|
|
|
1274
1288
|
</div>
|
|
1275
1289
|
</div>
|
|
1276
1290
|
|
|
1291
|
+
<div id="posture-fields" class="hidden">
|
|
1292
|
+
<div class="note">Security Posture Scan checks endpoint configuration, auth exposure, headers, transport, and operational security signals. This matches the web Security Checkup flow.</div>
|
|
1293
|
+
<div class="two">
|
|
1294
|
+
<div>
|
|
1295
|
+
<label>AI endpoint URL</label>
|
|
1296
|
+
<input id="postureUrl" placeholder="https://your-ai-endpoint.com/api/chat" />
|
|
1297
|
+
</div>
|
|
1298
|
+
<div>
|
|
1299
|
+
<label>HTTP method</label>
|
|
1300
|
+
<select id="postureMethod"><option value="POST" selected>POST</option><option value="GET">GET</option></select>
|
|
1301
|
+
</div>
|
|
1302
|
+
</div>
|
|
1303
|
+
<div class="two">
|
|
1304
|
+
<div>
|
|
1305
|
+
<label>Auth type</label>
|
|
1306
|
+
<select id="postureAuthType"><option value="none" selected>No Auth</option><option value="bearer">Bearer Token</option><option value="api-key">API Key</option></select>
|
|
1307
|
+
</div>
|
|
1308
|
+
<div>
|
|
1309
|
+
<label>Token / API key value</label>
|
|
1310
|
+
<input id="postureToken" type="password" placeholder="Optional" />
|
|
1311
|
+
</div>
|
|
1312
|
+
</div>
|
|
1313
|
+
</div>
|
|
1314
|
+
|
|
1277
1315
|
<div id="rag-fields" class="hidden">
|
|
1278
1316
|
<label>RAG retrieval URL</label>
|
|
1279
1317
|
<input id="ragUrl" placeholder="http://127.0.0.1:8787/rag/retrieve" />
|
|
@@ -1371,27 +1409,31 @@ Local targets stay on this machine; saved scan reports go to Full Court Defense.
|
|
|
1371
1409
|
</section>
|
|
1372
1410
|
</main>
|
|
1373
1411
|
<script>
|
|
1412
|
+
const scanTargets = ['endpoint','posture','rag','mcp'];
|
|
1374
1413
|
let target = 'runner';
|
|
1375
1414
|
let step = 1;
|
|
1376
1415
|
const qs = new URLSearchParams(location.search);
|
|
1377
|
-
if (['runner','endpoint','rag','mcp'].includes(qs.get('target'))) target = qs.get('target');
|
|
1416
|
+
if (['runner','endpoint','posture','rag','mcp','help'].includes(qs.get('target'))) target = qs.get('target');
|
|
1378
1417
|
const byId = id => document.getElementById(id);
|
|
1418
|
+
const workflow = () => scanTargets.includes(target) ? 'scan' : target;
|
|
1379
1419
|
const sync = () => {
|
|
1380
|
-
document.querySelectorAll('.
|
|
1381
|
-
|
|
1382
|
-
|
|
1420
|
+
document.querySelectorAll('.workflowCard').forEach(c => c.classList.toggle('active', c.dataset.workflow === workflow()));
|
|
1421
|
+
document.querySelectorAll('.scanTypeCard').forEach(c => c.classList.toggle('active', c.dataset.target === target));
|
|
1422
|
+
['runner','endpoint','posture','rag','mcp','help'].forEach(id => byId(id + '-fields').classList.toggle('hidden', id !== target));
|
|
1423
|
+
const scanTarget = scanTargets.includes(target);
|
|
1424
|
+
byId('scan-picker').classList.toggle('hidden', !scanTarget);
|
|
1383
1425
|
byId('scanSteps').classList.toggle('hidden', !scanTarget);
|
|
1384
1426
|
byId('scanActions').classList.toggle('hidden', !scanTarget);
|
|
1385
1427
|
byId('connectTitle').textContent = target === 'runner'
|
|
1386
1428
|
? 'Run MCP Tool'
|
|
1387
1429
|
: target === 'help'
|
|
1388
1430
|
? 'Local workflow options'
|
|
1389
|
-
: '
|
|
1431
|
+
: 'Choose scan target';
|
|
1390
1432
|
byId('connectSubtitle').textContent = target === 'runner'
|
|
1391
1433
|
? 'Call one MCP tool from this machine. This is not a scan.'
|
|
1392
1434
|
: target === 'help'
|
|
1393
1435
|
? 'Choose one-off tool run or local red-team scan.'
|
|
1394
|
-
: '
|
|
1436
|
+
: 'Select Endpoint, RAG, or MCP, then connect the internal target.';
|
|
1395
1437
|
if (!scanTarget) setStep(1);
|
|
1396
1438
|
};
|
|
1397
1439
|
const setStep = next => {
|
|
@@ -1402,7 +1444,14 @@ Local targets stay on this machine; saved scan reports go to Full Court Defense.
|
|
|
1402
1444
|
byId('step-dot-' + n).classList.toggle('done', n < step);
|
|
1403
1445
|
});
|
|
1404
1446
|
};
|
|
1405
|
-
document.querySelectorAll('.
|
|
1447
|
+
document.querySelectorAll('.workflowCard').forEach(c => c.onclick = () => {
|
|
1448
|
+
target = c.dataset.workflow === 'scan' ? 'endpoint' : c.dataset.workflow;
|
|
1449
|
+
sync();
|
|
1450
|
+
});
|
|
1451
|
+
document.querySelectorAll('.scanTypeCard').forEach(c => c.onclick = () => {
|
|
1452
|
+
target = c.dataset.target;
|
|
1453
|
+
sync();
|
|
1454
|
+
});
|
|
1406
1455
|
sync();
|
|
1407
1456
|
byId('ragUrl').value = qs.get('ragUrl') || '';
|
|
1408
1457
|
byId('ragPath').value = qs.get('ragPath') || '';
|
|
@@ -1418,6 +1467,7 @@ Local targets stay on this machine; saved scan reports go to Full Court Defense.
|
|
|
1418
1467
|
const payload = () => ({
|
|
1419
1468
|
target, mode: byId('mode').value, format: byId('format').value,
|
|
1420
1469
|
endpoint: byId('endpoint').value, inputField: byId('inputField').value, outputField: byId('outputField').value,
|
|
1470
|
+
postureUrl: byId('postureUrl').value, postureMethod: byId('postureMethod').value, postureAuthType: byId('postureAuthType').value, postureToken: byId('postureToken').value,
|
|
1421
1471
|
ragUrl: byId('ragUrl').value, ragPath: byId('ragPath').value, ragInputField: byId('ragInputField').value, ragOutputField: byId('ragOutputField').value,
|
|
1422
1472
|
mcpUrl: byId('mcpUrl').value, mcpCommand: byId('mcpCommand').value, mcpArgs: byId('mcpArgs').value, mcpTool: byId('mcpTool').value
|
|
1423
1473
|
});
|
|
@@ -1453,6 +1503,7 @@ Local targets stay on this machine; saved scan reports go to Full Court Defense.
|
|
|
1453
1503
|
const renderCommand = p => {
|
|
1454
1504
|
const parts = ['fullcourtdefense scan --local --type', p.target, '--mode', p.mode, '--format', p.format, '--no-open'];
|
|
1455
1505
|
if (p.target === 'endpoint') parts.push('--endpoint', JSON.stringify(p.endpoint), '--method POST --request-format custom --input-field', p.inputField || 'message', '--output-field', p.outputField || 'response');
|
|
1506
|
+
if (p.target === 'posture') return 'Security Posture Scan runs through this local web UI and calls the Full Court Defense posture checkup API for ' + (p.postureUrl || '<endpoint-url>');
|
|
1456
1507
|
if (p.target === 'rag') { if (p.ragUrl) parts.push('--rag-url', JSON.stringify(p.ragUrl), '--method POST --request-format custom --input-field', p.ragInputField || 'query', '--output-field', p.ragOutputField || 'chunks'); else parts.push('--rag-path', JSON.stringify(p.ragPath || '.\\\\docs')); }
|
|
1457
1508
|
if (p.target === 'mcp') { if (p.mcpUrl) parts.push('--mcp-url', JSON.stringify(p.mcpUrl)); else parts.push('--mcp-command', p.mcpCommand || 'node', '--mcp-args', JSON.stringify(p.mcpArgs || '.\\\\server.js')); parts.push('--mcp-tool', p.mcpTool || 'all'); }
|
|
1458
1509
|
return parts.join(' ');
|
|
@@ -1508,6 +1559,10 @@ Local targets stay on this machine; saved scan reports go to Full Court Defense.
|
|
|
1508
1559
|
sync();
|
|
1509
1560
|
setStep(1);
|
|
1510
1561
|
byId('endpoint').value = '';
|
|
1562
|
+
byId('postureUrl').value = '';
|
|
1563
|
+
byId('postureMethod').value = 'POST';
|
|
1564
|
+
byId('postureAuthType').value = 'none';
|
|
1565
|
+
byId('postureToken').value = '';
|
|
1511
1566
|
byId('inputField').value = 'message';
|
|
1512
1567
|
byId('outputField').value = 'response';
|
|
1513
1568
|
byId('ragUrl').value = '';
|
|
@@ -1712,6 +1767,20 @@ Local targets stay on this machine; saved scan reports go to Full Court Defense.
|
|
|
1712
1767
|
byId('run').textContent = 'Run local scan';
|
|
1713
1768
|
return;
|
|
1714
1769
|
}
|
|
1770
|
+
if (target === 'posture') {
|
|
1771
|
+
try {
|
|
1772
|
+
setConsole('Starting Security Posture Scan...\\n', 'Running posture');
|
|
1773
|
+
const postureRes = await fetch('/posture/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload()) });
|
|
1774
|
+
const postureData = await postureRes.json();
|
|
1775
|
+
setConsole(JSON.stringify(postureData, null, 2), postureData.ok ? 'Posture complete' : 'Posture failed');
|
|
1776
|
+
} catch (error) {
|
|
1777
|
+
setConsole('Security Posture Scan failed: ' + (error && error.message ? error.message : String(error)), 'Posture failed');
|
|
1778
|
+
} finally {
|
|
1779
|
+
byId('run').disabled = false;
|
|
1780
|
+
byId('run').textContent = 'Run local scan';
|
|
1781
|
+
}
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1715
1784
|
setConsole('Starting local red-team scan...\\n', 'Running');
|
|
1716
1785
|
const res = await fetch('/run', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload()) });
|
|
1717
1786
|
if (!res.body) {
|
|
@@ -1824,6 +1893,40 @@ async function runCliFromBrowser(body, onChunk) {
|
|
|
1824
1893
|
});
|
|
1825
1894
|
});
|
|
1826
1895
|
}
|
|
1896
|
+
async function runPostureScanFromBrowser(args, body) {
|
|
1897
|
+
const auth = currentShieldConfig(args);
|
|
1898
|
+
const apiUrl = (auth.apiUrl || DEFAULT_API_URL).replace(/\/$/, '');
|
|
1899
|
+
const endpoint = typeof body.postureUrl === 'string' ? body.postureUrl.trim() : '';
|
|
1900
|
+
const method = body.postureMethod === 'GET' ? 'GET' : 'POST';
|
|
1901
|
+
const authType = typeof body.postureAuthType === 'string' ? body.postureAuthType : 'none';
|
|
1902
|
+
const token = typeof body.postureToken === 'string' ? body.postureToken.trim() : '';
|
|
1903
|
+
if (!endpoint)
|
|
1904
|
+
throw new Error('AI endpoint URL is required for Security Posture Scan.');
|
|
1905
|
+
const headers = {};
|
|
1906
|
+
if (authType === 'bearer' && token)
|
|
1907
|
+
headers.Authorization = `Bearer ${token}`;
|
|
1908
|
+
if (authType === 'api-key' && token)
|
|
1909
|
+
headers['X-API-Key'] = token;
|
|
1910
|
+
const response = await fetch(`${apiUrl}/api/posture/scan/public`, {
|
|
1911
|
+
method: 'POST',
|
|
1912
|
+
headers: { 'content-type': 'application/json' },
|
|
1913
|
+
body: JSON.stringify({
|
|
1914
|
+
endpoint,
|
|
1915
|
+
method,
|
|
1916
|
+
scanMode: 'passive',
|
|
1917
|
+
...(Object.keys(headers).length ? { headers } : {}),
|
|
1918
|
+
}),
|
|
1919
|
+
signal: AbortSignal.timeout(60000),
|
|
1920
|
+
});
|
|
1921
|
+
const text = await response.text();
|
|
1922
|
+
return {
|
|
1923
|
+
ok: response.ok,
|
|
1924
|
+
status: response.status,
|
|
1925
|
+
endpoint,
|
|
1926
|
+
scanType: 'security_posture',
|
|
1927
|
+
result: text,
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1827
1930
|
function createMcpClient(options) {
|
|
1828
1931
|
if (options.transport === 'sse')
|
|
1829
1932
|
return new SseMcpClient(options);
|
|
@@ -1990,6 +2093,13 @@ function startCompactWebServer(args) {
|
|
|
1990
2093
|
res.end(JSON.stringify(result));
|
|
1991
2094
|
return;
|
|
1992
2095
|
}
|
|
2096
|
+
if (req.method === 'POST' && url.pathname === '/posture/run') {
|
|
2097
|
+
const body = await readRequestBody(req);
|
|
2098
|
+
const result = await runPostureScanFromBrowser(args, body);
|
|
2099
|
+
res.writeHead(result.ok ? 200 : 400, { 'content-type': 'application/json; charset=utf-8' });
|
|
2100
|
+
res.end(JSON.stringify(result));
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
1993
2103
|
if (req.method === 'POST' && url.pathname === '/run') {
|
|
1994
2104
|
const body = await readRequestBody(req);
|
|
1995
2105
|
res.writeHead(200, {
|
package/dist/version.json
CHANGED