ship-safe 6.1.0 → 6.2.0
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/README.md +735 -594
- package/cli/agents/api-fuzzer.js +345 -345
- package/cli/agents/auth-bypass-agent.js +348 -348
- package/cli/agents/base-agent.js +272 -272
- package/cli/agents/cicd-scanner.js +236 -201
- package/cli/agents/config-auditor.js +521 -521
- package/cli/agents/deep-analyzer.js +6 -2
- package/cli/agents/git-history-scanner.js +170 -170
- package/cli/agents/html-reporter.js +40 -4
- package/cli/agents/index.js +84 -84
- package/cli/agents/injection-tester.js +500 -500
- package/cli/agents/llm-redteam.js +251 -251
- package/cli/agents/mobile-scanner.js +231 -231
- package/cli/agents/orchestrator.js +322 -322
- package/cli/agents/pii-compliance-agent.js +301 -301
- package/cli/agents/scoring-engine.js +248 -248
- package/cli/agents/supabase-rls-agent.js +154 -154
- package/cli/agents/supply-chain-agent.js +650 -507
- package/cli/bin/ship-safe.js +452 -426
- package/cli/commands/agent.js +608 -608
- package/cli/commands/audit.js +986 -979
- package/cli/commands/baseline.js +193 -193
- package/cli/commands/ci.js +342 -342
- package/cli/commands/deps.js +516 -516
- package/cli/commands/doctor.js +159 -159
- package/cli/commands/fix.js +218 -218
- package/cli/commands/hooks.js +268 -0
- package/cli/commands/init.js +407 -407
- package/cli/commands/mcp.js +304 -304
- package/cli/commands/red-team.js +7 -1
- package/cli/commands/remediate.js +798 -798
- package/cli/commands/rotate.js +571 -571
- package/cli/commands/scan.js +569 -567
- package/cli/commands/score.js +449 -448
- package/cli/commands/watch.js +281 -281
- package/cli/hooks/patterns.js +313 -0
- package/cli/hooks/post-tool-use.js +140 -0
- package/cli/hooks/pre-tool-use.js +186 -0
- package/cli/index.js +73 -69
- package/cli/providers/llm-provider.js +397 -287
- package/cli/utils/autofix-rules.js +74 -74
- package/cli/utils/cache-manager.js +311 -311
- package/cli/utils/output.js +1 -0
- package/cli/utils/patterns.js +1121 -1121
- package/cli/utils/pdf-generator.js +94 -94
- package/package.json +69 -68
- package/cli/__tests__/agents.test.js +0 -1301
- package/configs/supabase/rls-templates.sql +0 -242
|
@@ -100,8 +100,12 @@ export class DeepAnalyzer {
|
|
|
100
100
|
return new DeepAnalyzer({ provider, ...options });
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
// Auto-detect from env
|
|
104
|
-
const provider = autoDetectProvider(rootPath
|
|
103
|
+
// Auto-detect from env, honouring explicit --provider / --base-url / --model
|
|
104
|
+
const provider = autoDetectProvider(rootPath, {
|
|
105
|
+
provider: options.provider,
|
|
106
|
+
baseUrl: options.baseUrl,
|
|
107
|
+
model: options.model,
|
|
108
|
+
});
|
|
105
109
|
if (!provider) return null;
|
|
106
110
|
|
|
107
111
|
return new DeepAnalyzer({ provider, ...options });
|
|
@@ -1,170 +1,170 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHistoryScanner Agent
|
|
3
|
-
* ========================
|
|
4
|
-
*
|
|
5
|
-
* Scans git commit history for secrets that were committed
|
|
6
|
-
* and later removed but remain in repository history.
|
|
7
|
-
* These are the most dangerous secrets — developers think
|
|
8
|
-
* they're deleted but they're still accessible.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { execSync, execFileSync } from 'child_process';
|
|
12
|
-
import path from 'path';
|
|
13
|
-
import { BaseAgent, createFinding } from './base-agent.js';
|
|
14
|
-
import { SECRET_PATTERNS } from '../utils/patterns.js';
|
|
15
|
-
|
|
16
|
-
// Compile a fast combined regex from all secret patterns
|
|
17
|
-
const FAST_SECRET_PATTERNS = SECRET_PATTERNS.map(p => ({
|
|
18
|
-
name: p.name,
|
|
19
|
-
pattern: p.pattern,
|
|
20
|
-
severity: p.severity,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
export class GitHistoryScanner extends BaseAgent {
|
|
24
|
-
constructor() {
|
|
25
|
-
super('GitHistoryScanner', 'Scan git history for leaked secrets', 'history');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async analyze(context) {
|
|
29
|
-
const { rootPath, options } = context;
|
|
30
|
-
const findings = [];
|
|
31
|
-
|
|
32
|
-
// Check if this is a git repository
|
|
33
|
-
if (!this.isGitRepo(rootPath)) return [];
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
// Get recent commits (default: last 50, configurable)
|
|
37
|
-
const maxCommits = options?.maxCommits || 50;
|
|
38
|
-
const since = options?.since || null;
|
|
39
|
-
|
|
40
|
-
const gitArgs = ['-C', rootPath, 'log', '--all', '--diff-filter=A', '--diff-filter=M', '-p', '--no-color', `--max-count=${parseInt(maxCommits, 10)}`];
|
|
41
|
-
if (since) {
|
|
42
|
-
gitArgs.push(`--since=${since}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let diffOutput;
|
|
46
|
-
try {
|
|
47
|
-
diffOutput = execFileSync('git', gitArgs, {
|
|
48
|
-
cwd: rootPath,
|
|
49
|
-
encoding: 'utf-8',
|
|
50
|
-
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
51
|
-
timeout: 60000, // 60s timeout
|
|
52
|
-
});
|
|
53
|
-
} catch {
|
|
54
|
-
// git log failed — might be a shallow clone or no history
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!diffOutput) return [];
|
|
59
|
-
|
|
60
|
-
// Parse the diff output
|
|
61
|
-
let currentFile = '';
|
|
62
|
-
let currentCommit = '';
|
|
63
|
-
let currentDate = '';
|
|
64
|
-
const lines = diffOutput.split('\n');
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < lines.length; i++) {
|
|
67
|
-
const line = lines[i];
|
|
68
|
-
|
|
69
|
-
// Track current commit
|
|
70
|
-
if (line.startsWith('commit ')) {
|
|
71
|
-
currentCommit = line.slice(7, 17); // First 10 chars of hash
|
|
72
|
-
}
|
|
73
|
-
if (line.startsWith('Date:')) {
|
|
74
|
-
currentDate = line.slice(5).trim();
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Track current file
|
|
78
|
-
if (line.startsWith('diff --git ')) {
|
|
79
|
-
const match = line.match(/diff --git a\/(.+) b\//);
|
|
80
|
-
if (match) currentFile = match[1];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Only check added lines (lines starting with +)
|
|
84
|
-
if (!line.startsWith('+') || line.startsWith('+++')) continue;
|
|
85
|
-
|
|
86
|
-
const addedLine = line.slice(1); // Remove the leading +
|
|
87
|
-
|
|
88
|
-
// Check against all secret patterns
|
|
89
|
-
for (const p of FAST_SECRET_PATTERNS) {
|
|
90
|
-
p.pattern.lastIndex = 0;
|
|
91
|
-
const match = p.pattern.exec(addedLine);
|
|
92
|
-
if (match) {
|
|
93
|
-
// Check if this secret still exists in current working tree
|
|
94
|
-
const stillExists = this.existsInWorkingTree(rootPath, match[0]);
|
|
95
|
-
|
|
96
|
-
findings.push(createFinding({
|
|
97
|
-
file: path.join(rootPath, currentFile),
|
|
98
|
-
line: 0, // Line number not meaningful in history
|
|
99
|
-
severity: stillExists ? p.severity : this.elevateSeverity(p.severity),
|
|
100
|
-
category: 'history',
|
|
101
|
-
rule: 'GIT_HISTORY_SECRET',
|
|
102
|
-
title: `Historical Secret: ${p.name}`,
|
|
103
|
-
description: stillExists
|
|
104
|
-
? `Secret found in current code AND in git history (commit ${currentCommit}).`
|
|
105
|
-
: `Secret was removed from code but still exists in git history (commit ${currentCommit}, ${currentDate}). Anyone with repo access can retrieve it.`,
|
|
106
|
-
matched: this.maskSecret(match[0]),
|
|
107
|
-
confidence: 'high',
|
|
108
|
-
fix: stillExists
|
|
109
|
-
? 'Remove from code, rotate the credential, then clean git history with BFG or git filter-repo'
|
|
110
|
-
: 'Rotate this credential immediately, then clean history: npx bfg --replace-text passwords.txt',
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Deduplicate by matched value (same secret in multiple commits)
|
|
117
|
-
const seen = new Set();
|
|
118
|
-
return findings.filter(f => {
|
|
119
|
-
const key = `${f.matched}:${f.title}`;
|
|
120
|
-
if (seen.has(key)) return false;
|
|
121
|
-
seen.add(key);
|
|
122
|
-
return true;
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
} catch (err) {
|
|
126
|
-
// Don't fail the entire scan if git history scan fails
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
isGitRepo(dir) {
|
|
132
|
-
try {
|
|
133
|
-
execSync('git rev-parse --is-inside-work-tree', { cwd: dir, stdio: 'pipe' });
|
|
134
|
-
return true;
|
|
135
|
-
} catch {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
existsInWorkingTree(rootPath, secret) {
|
|
141
|
-
try {
|
|
142
|
-
const result = execFileSync('git', [
|
|
143
|
-
'-C', rootPath, 'grep', '-l', secret.slice(0, 12),
|
|
144
|
-
'--', '*.js', '*.ts', '*.py', '*.env', '*.json'
|
|
145
|
-
], {
|
|
146
|
-
cwd: rootPath,
|
|
147
|
-
encoding: 'utf-8',
|
|
148
|
-
timeout: 5000,
|
|
149
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
150
|
-
});
|
|
151
|
-
return result.trim().length > 0;
|
|
152
|
-
} catch {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
elevateSeverity(sev) {
|
|
158
|
-
// Secrets in history-only are MORE dangerous (developer thinks they're gone)
|
|
159
|
-
if (sev === 'medium') return 'high';
|
|
160
|
-
if (sev === 'high') return 'critical';
|
|
161
|
-
return sev;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
maskSecret(secret) {
|
|
165
|
-
if (secret.length <= 10) return secret.slice(0, 4) + '***';
|
|
166
|
-
return secret.slice(0, 8) + '***' + secret.slice(-4);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export default GitHistoryScanner;
|
|
1
|
+
/**
|
|
2
|
+
* GitHistoryScanner Agent
|
|
3
|
+
* ========================
|
|
4
|
+
*
|
|
5
|
+
* Scans git commit history for secrets that were committed
|
|
6
|
+
* and later removed but remain in repository history.
|
|
7
|
+
* These are the most dangerous secrets — developers think
|
|
8
|
+
* they're deleted but they're still accessible.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync, execFileSync } from 'child_process';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { BaseAgent, createFinding } from './base-agent.js';
|
|
14
|
+
import { SECRET_PATTERNS } from '../utils/patterns.js';
|
|
15
|
+
|
|
16
|
+
// Compile a fast combined regex from all secret patterns
|
|
17
|
+
const FAST_SECRET_PATTERNS = SECRET_PATTERNS.map(p => ({
|
|
18
|
+
name: p.name,
|
|
19
|
+
pattern: p.pattern,
|
|
20
|
+
severity: p.severity,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
export class GitHistoryScanner extends BaseAgent {
|
|
24
|
+
constructor() {
|
|
25
|
+
super('GitHistoryScanner', 'Scan git history for leaked secrets', 'history');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async analyze(context) {
|
|
29
|
+
const { rootPath, options } = context;
|
|
30
|
+
const findings = [];
|
|
31
|
+
|
|
32
|
+
// Check if this is a git repository
|
|
33
|
+
if (!this.isGitRepo(rootPath)) return [];
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
// Get recent commits (default: last 50, configurable)
|
|
37
|
+
const maxCommits = options?.maxCommits || 50;
|
|
38
|
+
const since = options?.since || null;
|
|
39
|
+
|
|
40
|
+
const gitArgs = ['-C', rootPath, 'log', '--all', '--diff-filter=A', '--diff-filter=M', '-p', '--no-color', `--max-count=${parseInt(maxCommits, 10)}`];
|
|
41
|
+
if (since) {
|
|
42
|
+
gitArgs.push(`--since=${since}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let diffOutput;
|
|
46
|
+
try {
|
|
47
|
+
diffOutput = execFileSync('git', gitArgs, {
|
|
48
|
+
cwd: rootPath,
|
|
49
|
+
encoding: 'utf-8',
|
|
50
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
51
|
+
timeout: 60000, // 60s timeout
|
|
52
|
+
});
|
|
53
|
+
} catch {
|
|
54
|
+
// git log failed — might be a shallow clone or no history
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!diffOutput) return [];
|
|
59
|
+
|
|
60
|
+
// Parse the diff output
|
|
61
|
+
let currentFile = '';
|
|
62
|
+
let currentCommit = '';
|
|
63
|
+
let currentDate = '';
|
|
64
|
+
const lines = diffOutput.split('\n');
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < lines.length; i++) {
|
|
67
|
+
const line = lines[i];
|
|
68
|
+
|
|
69
|
+
// Track current commit
|
|
70
|
+
if (line.startsWith('commit ')) {
|
|
71
|
+
currentCommit = line.slice(7, 17); // First 10 chars of hash
|
|
72
|
+
}
|
|
73
|
+
if (line.startsWith('Date:')) {
|
|
74
|
+
currentDate = line.slice(5).trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Track current file
|
|
78
|
+
if (line.startsWith('diff --git ')) {
|
|
79
|
+
const match = line.match(/diff --git a\/(.+) b\//);
|
|
80
|
+
if (match) currentFile = match[1];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Only check added lines (lines starting with +)
|
|
84
|
+
if (!line.startsWith('+') || line.startsWith('+++')) continue;
|
|
85
|
+
|
|
86
|
+
const addedLine = line.slice(1); // Remove the leading +
|
|
87
|
+
|
|
88
|
+
// Check against all secret patterns
|
|
89
|
+
for (const p of FAST_SECRET_PATTERNS) {
|
|
90
|
+
p.pattern.lastIndex = 0;
|
|
91
|
+
const match = p.pattern.exec(addedLine);
|
|
92
|
+
if (match) {
|
|
93
|
+
// Check if this secret still exists in current working tree
|
|
94
|
+
const stillExists = this.existsInWorkingTree(rootPath, match[0]);
|
|
95
|
+
|
|
96
|
+
findings.push(createFinding({
|
|
97
|
+
file: path.join(rootPath, currentFile),
|
|
98
|
+
line: 0, // Line number not meaningful in history
|
|
99
|
+
severity: stillExists ? p.severity : this.elevateSeverity(p.severity),
|
|
100
|
+
category: 'history',
|
|
101
|
+
rule: 'GIT_HISTORY_SECRET',
|
|
102
|
+
title: `Historical Secret: ${p.name}`,
|
|
103
|
+
description: stillExists
|
|
104
|
+
? `Secret found in current code AND in git history (commit ${currentCommit}).`
|
|
105
|
+
: `Secret was removed from code but still exists in git history (commit ${currentCommit}, ${currentDate}). Anyone with repo access can retrieve it.`,
|
|
106
|
+
matched: this.maskSecret(match[0]),
|
|
107
|
+
confidence: 'high',
|
|
108
|
+
fix: stillExists
|
|
109
|
+
? 'Remove from code, rotate the credential, then clean git history with BFG or git filter-repo'
|
|
110
|
+
: 'Rotate this credential immediately, then clean history: npx bfg --replace-text passwords.txt',
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Deduplicate by matched value (same secret in multiple commits)
|
|
117
|
+
const seen = new Set();
|
|
118
|
+
return findings.filter(f => {
|
|
119
|
+
const key = `${f.matched}:${f.title}`;
|
|
120
|
+
if (seen.has(key)) return false;
|
|
121
|
+
seen.add(key);
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
} catch (err) {
|
|
126
|
+
// Don't fail the entire scan if git history scan fails
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
isGitRepo(dir) {
|
|
132
|
+
try {
|
|
133
|
+
execSync('git rev-parse --is-inside-work-tree', { cwd: dir, stdio: 'pipe' });
|
|
134
|
+
return true;
|
|
135
|
+
} catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
existsInWorkingTree(rootPath, secret) {
|
|
141
|
+
try {
|
|
142
|
+
const result = execFileSync('git', [
|
|
143
|
+
'-C', rootPath, 'grep', '-l', secret.slice(0, 12),
|
|
144
|
+
'--', '*.js', '*.ts', '*.py', '*.env', '*.json'
|
|
145
|
+
], {
|
|
146
|
+
cwd: rootPath,
|
|
147
|
+
encoding: 'utf-8',
|
|
148
|
+
timeout: 5000,
|
|
149
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
150
|
+
});
|
|
151
|
+
return result.trim().length > 0;
|
|
152
|
+
} catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
elevateSeverity(sev) {
|
|
158
|
+
// Secrets in history-only are MORE dangerous (developer thinks they're gone)
|
|
159
|
+
if (sev === 'medium') return 'high';
|
|
160
|
+
if (sev === 'high') return 'critical';
|
|
161
|
+
return sev;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
maskSecret(secret) {
|
|
165
|
+
if (secret.length <= 10) return secret.slice(0, 4) + '***';
|
|
166
|
+
return secret.slice(0, 8) + '***' + secret.slice(-4);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export default GitHistoryScanner;
|
|
@@ -154,9 +154,14 @@ small{color:#64748b}
|
|
|
154
154
|
</tbody>
|
|
155
155
|
</table>` : ''}
|
|
156
156
|
|
|
157
|
+
<div style="text-align:center;margin:1.5rem 0">
|
|
158
|
+
<a href="https://twitter.com/intent/tweet?text=${encodeURIComponent(`My project scored ${scoreResult.score}/100 (Grade ${scoreResult.grade.letter}) on Ship Safe! Scan yours: npx ship-safe audit . https://shipsafecli.com`)}" target="_blank" rel="noopener" style="display:inline-block;padding:8px 18px;background:#000;color:#fff;border-radius:6px;font-size:0.85rem;font-weight:600;text-decoration:none;margin:0 4px">Share on X</a>
|
|
159
|
+
<a href="https://www.linkedin.com/sharing/share-offsite/?url=https://shipsafecli.com" target="_blank" rel="noopener" style="display:inline-block;padding:8px 18px;background:#0a66c2;color:#fff;border-radius:6px;font-size:0.85rem;font-weight:600;text-decoration:none;margin:0 4px">Share on LinkedIn</a>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
157
162
|
<div class="footer">
|
|
158
|
-
Generated by <strong>Ship Safe v6.
|
|
159
|
-
<a href="https://shipsafecli.com" style="color:#38bdf8">shipsafecli.com</a>
|
|
163
|
+
Generated by <strong>Ship Safe v6.1</strong> — Security toolkit for developers<br>
|
|
164
|
+
<a href="https://shipsafecli.com" style="color:#38bdf8">shipsafecli.com</a> · <a href="https://shipsafecli.com/pricing" style="color:#38bdf8">Cloud Dashboard</a>
|
|
160
165
|
</div>
|
|
161
166
|
</div>
|
|
162
167
|
</body>
|
|
@@ -340,6 +345,16 @@ small{color:#94a3b8}
|
|
|
340
345
|
.toc a:hover{text-decoration:underline}
|
|
341
346
|
.footer{text-align:center;color:#475569;margin-top:3rem;padding:2rem;border-top:1px solid #1e293b}
|
|
342
347
|
.footer a{color:#38bdf8}
|
|
348
|
+
.share-bar{display:flex;justify-content:center;gap:0.75rem;margin:1.5rem 0}
|
|
349
|
+
.share-btn{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border-radius:6px;font-size:0.85rem;font-weight:600;text-decoration:none;cursor:pointer;border:none;transition:opacity .15s}
|
|
350
|
+
.share-btn:hover{opacity:0.85}
|
|
351
|
+
.share-btn-x{background:#000;color:#fff}
|
|
352
|
+
.share-btn-li{background:#0a66c2;color:#fff}
|
|
353
|
+
.share-btn-copy{background:#334155;color:#38bdf8;border:1px solid #475569}
|
|
354
|
+
.share-btn-copy.copied{background:#22c55e33;color:#22c55e;border-color:#22c55e}
|
|
355
|
+
.badge-section{background:#1e293b;border-radius:8px;padding:1.5rem;margin-top:1.5rem;text-align:center}
|
|
356
|
+
.badge-section img{margin-bottom:0.75rem}
|
|
357
|
+
.badge-section code{display:block;background:#0f172a;padding:8px 12px;border-radius:4px;font-size:0.75rem;margin-top:0.5rem;word-break:break-all;user-select:all}
|
|
343
358
|
/* Hidden row */
|
|
344
359
|
.hidden-row{display:none}
|
|
345
360
|
/* Print */
|
|
@@ -445,13 +460,34 @@ small{color:#94a3b8}
|
|
|
445
460
|
</tbody>
|
|
446
461
|
</table>` : ''}
|
|
447
462
|
|
|
463
|
+
<div class="share-bar">
|
|
464
|
+
<a class="share-btn share-btn-x" href="https://twitter.com/intent/tweet?text=${encodeURIComponent(`My project scored ${scoreResult.score}/100 (Grade ${scoreResult.grade.letter}) on Ship Safe security audit! 18 AI agents, 80+ attack classes.\n\nScan yours: npx ship-safe audit .\n\nhttps://shipsafecli.com`)}" target="_blank" rel="noopener">Share on X</a>
|
|
465
|
+
<a class="share-btn share-btn-li" href="https://www.linkedin.com/sharing/share-offsite/?url=https://shipsafecli.com" target="_blank" rel="noopener">Share on LinkedIn</a>
|
|
466
|
+
<button class="share-btn share-btn-copy" onclick="copyShareText(this)">Copy Score Summary</button>
|
|
467
|
+
</div>
|
|
468
|
+
|
|
469
|
+
<div class="badge-section">
|
|
470
|
+
<p style="color:#94a3b8;margin-bottom:0.75rem"><strong>Add a security badge to your README:</strong></p>
|
|
471
|
+
<img src="https://img.shields.io/badge/Ship_Safe-${scoreResult.grade.letter}-${gradeColors[scoreResult.grade.letter].replace('#','')}" alt="Ship Safe Grade ${scoreResult.grade.letter}" />
|
|
472
|
+
<code>[})](https://shipsafecli.com)</code>
|
|
473
|
+
</div>
|
|
474
|
+
|
|
448
475
|
<div class="footer">
|
|
449
|
-
Generated by <strong>Ship Safe v6.
|
|
450
|
-
<a href="https://shipsafecli.com">shipsafecli.com</a>
|
|
476
|
+
Generated by <strong>Ship Safe v6.1</strong> — Full Security Audit<br>
|
|
477
|
+
<a href="https://shipsafecli.com">shipsafecli.com</a> · <a href="https://shipsafecli.com/pricing">Cloud Dashboard</a>
|
|
451
478
|
</div>
|
|
452
479
|
</div>
|
|
453
480
|
|
|
454
481
|
<script>
|
|
482
|
+
function copyShareText(btn) {
|
|
483
|
+
const text = 'My project scored ${scoreResult.score}/100 (Grade ${scoreResult.grade.letter}) on Ship Safe security audit!\\nScan yours: npx ship-safe audit .\\nhttps://shipsafecli.com';
|
|
484
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
485
|
+
btn.textContent = 'Copied!';
|
|
486
|
+
btn.classList.add('copied');
|
|
487
|
+
setTimeout(() => { btn.textContent = 'Copy Score Summary'; btn.classList.remove('copied'); }, 2000);
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
455
491
|
// ── Severity filter ────────────────────────────────────────────────────────
|
|
456
492
|
let activeSev = 'all';
|
|
457
493
|
let searchTerm = '';
|
package/cli/agents/index.js
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent Registry
|
|
3
|
-
* ===============
|
|
4
|
-
*
|
|
5
|
-
* Central export of all agents and supporting classes.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export { BaseAgent, createFinding } from './base-agent.js';
|
|
9
|
-
export { Orchestrator } from './orchestrator.js';
|
|
10
|
-
export { ReconAgent } from './recon-agent.js';
|
|
11
|
-
export { InjectionTester } from './injection-tester.js';
|
|
12
|
-
export { AuthBypassAgent } from './auth-bypass-agent.js';
|
|
13
|
-
export { SSRFProber } from './ssrf-prober.js';
|
|
14
|
-
export { SupplyChainAudit } from './supply-chain-agent.js';
|
|
15
|
-
export { ConfigAuditor } from './config-auditor.js';
|
|
16
|
-
export { LLMRedTeam } from './llm-redteam.js';
|
|
17
|
-
export { MobileScanner } from './mobile-scanner.js';
|
|
18
|
-
export { GitHistoryScanner } from './git-history-scanner.js';
|
|
19
|
-
export { CICDScanner } from './cicd-scanner.js';
|
|
20
|
-
export { APIFuzzer } from './api-fuzzer.js';
|
|
21
|
-
export { SupabaseRLSAgent } from './supabase-rls-agent.js';
|
|
22
|
-
export { MCPSecurityAgent } from './mcp-security-agent.js';
|
|
23
|
-
export { AgenticSecurityAgent } from './agentic-security-agent.js';
|
|
24
|
-
export { RAGSecurityAgent } from './rag-security-agent.js';
|
|
25
|
-
export { PIIComplianceAgent } from './pii-compliance-agent.js';
|
|
26
|
-
export { VibeCodingAgent } from './vibe-coding-agent.js';
|
|
27
|
-
export { ExceptionHandlerAgent } from './exception-handler-agent.js';
|
|
28
|
-
export { AgentConfigScanner } from './agent-config-scanner.js';
|
|
29
|
-
export { ABOMGenerator } from './abom-generator.js';
|
|
30
|
-
export { VerifierAgent } from './verifier-agent.js';
|
|
31
|
-
export { DeepAnalyzer } from './deep-analyzer.js';
|
|
32
|
-
export { ScoringEngine, GRADES, CATEGORIES } from './scoring-engine.js';
|
|
33
|
-
export { SBOMGenerator } from './sbom-generator.js';
|
|
34
|
-
export { PolicyEngine } from './policy-engine.js';
|
|
35
|
-
export { HTMLReporter } from './html-reporter.js';
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Create a fully configured orchestrator with all 16 scanning agents.
|
|
39
|
-
* (VerifierAgent and DeepAnalyzer run as post-processors, not in the agent pool.)
|
|
40
|
-
*/
|
|
41
|
-
import { Orchestrator as OrchestratorClass } from './orchestrator.js';
|
|
42
|
-
import { InjectionTester as InjectionTesterClass } from './injection-tester.js';
|
|
43
|
-
import { AuthBypassAgent as AuthBypassAgentClass } from './auth-bypass-agent.js';
|
|
44
|
-
import { SSRFProber as SSRFProberClass } from './ssrf-prober.js';
|
|
45
|
-
import { SupplyChainAudit as SupplyChainAuditClass } from './supply-chain-agent.js';
|
|
46
|
-
import { ConfigAuditor as ConfigAuditorClass } from './config-auditor.js';
|
|
47
|
-
import { LLMRedTeam as LLMRedTeamClass } from './llm-redteam.js';
|
|
48
|
-
import { MobileScanner as MobileScannerClass } from './mobile-scanner.js';
|
|
49
|
-
import { GitHistoryScanner as GitHistoryScannerClass } from './git-history-scanner.js';
|
|
50
|
-
import { CICDScanner as CICDScannerClass } from './cicd-scanner.js';
|
|
51
|
-
import { APIFuzzer as APIFuzzerClass } from './api-fuzzer.js';
|
|
52
|
-
import { SupabaseRLSAgent as SupabaseRLSAgentClass } from './supabase-rls-agent.js';
|
|
53
|
-
import { MCPSecurityAgent as MCPSecurityAgentClass } from './mcp-security-agent.js';
|
|
54
|
-
import { AgenticSecurityAgent as AgenticSecurityAgentClass } from './agentic-security-agent.js';
|
|
55
|
-
import { RAGSecurityAgent as RAGSecurityAgentClass } from './rag-security-agent.js';
|
|
56
|
-
import { PIIComplianceAgent as PIIComplianceAgentClass } from './pii-compliance-agent.js';
|
|
57
|
-
import { VibeCodingAgent as VibeCodingAgentClass } from './vibe-coding-agent.js';
|
|
58
|
-
import { ExceptionHandlerAgent as ExceptionHandlerAgentClass } from './exception-handler-agent.js';
|
|
59
|
-
import { AgentConfigScanner as AgentConfigScannerClass } from './agent-config-scanner.js';
|
|
60
|
-
|
|
61
|
-
export function buildOrchestrator() {
|
|
62
|
-
const orchestrator = new OrchestratorClass();
|
|
63
|
-
orchestrator.registerAll([
|
|
64
|
-
new InjectionTesterClass(),
|
|
65
|
-
new AuthBypassAgentClass(),
|
|
66
|
-
new SSRFProberClass(),
|
|
67
|
-
new SupplyChainAuditClass(),
|
|
68
|
-
new ConfigAuditorClass(),
|
|
69
|
-
new LLMRedTeamClass(),
|
|
70
|
-
new MobileScannerClass(),
|
|
71
|
-
new GitHistoryScannerClass(),
|
|
72
|
-
new CICDScannerClass(),
|
|
73
|
-
new APIFuzzerClass(),
|
|
74
|
-
new SupabaseRLSAgentClass(),
|
|
75
|
-
new MCPSecurityAgentClass(),
|
|
76
|
-
new AgenticSecurityAgentClass(),
|
|
77
|
-
new RAGSecurityAgentClass(),
|
|
78
|
-
new PIIComplianceAgentClass(),
|
|
79
|
-
new VibeCodingAgentClass(),
|
|
80
|
-
new ExceptionHandlerAgentClass(),
|
|
81
|
-
new AgentConfigScannerClass(),
|
|
82
|
-
]);
|
|
83
|
-
return orchestrator;
|
|
84
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Agent Registry
|
|
3
|
+
* ===============
|
|
4
|
+
*
|
|
5
|
+
* Central export of all agents and supporting classes.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { BaseAgent, createFinding } from './base-agent.js';
|
|
9
|
+
export { Orchestrator } from './orchestrator.js';
|
|
10
|
+
export { ReconAgent } from './recon-agent.js';
|
|
11
|
+
export { InjectionTester } from './injection-tester.js';
|
|
12
|
+
export { AuthBypassAgent } from './auth-bypass-agent.js';
|
|
13
|
+
export { SSRFProber } from './ssrf-prober.js';
|
|
14
|
+
export { SupplyChainAudit } from './supply-chain-agent.js';
|
|
15
|
+
export { ConfigAuditor } from './config-auditor.js';
|
|
16
|
+
export { LLMRedTeam } from './llm-redteam.js';
|
|
17
|
+
export { MobileScanner } from './mobile-scanner.js';
|
|
18
|
+
export { GitHistoryScanner } from './git-history-scanner.js';
|
|
19
|
+
export { CICDScanner } from './cicd-scanner.js';
|
|
20
|
+
export { APIFuzzer } from './api-fuzzer.js';
|
|
21
|
+
export { SupabaseRLSAgent } from './supabase-rls-agent.js';
|
|
22
|
+
export { MCPSecurityAgent } from './mcp-security-agent.js';
|
|
23
|
+
export { AgenticSecurityAgent } from './agentic-security-agent.js';
|
|
24
|
+
export { RAGSecurityAgent } from './rag-security-agent.js';
|
|
25
|
+
export { PIIComplianceAgent } from './pii-compliance-agent.js';
|
|
26
|
+
export { VibeCodingAgent } from './vibe-coding-agent.js';
|
|
27
|
+
export { ExceptionHandlerAgent } from './exception-handler-agent.js';
|
|
28
|
+
export { AgentConfigScanner } from './agent-config-scanner.js';
|
|
29
|
+
export { ABOMGenerator } from './abom-generator.js';
|
|
30
|
+
export { VerifierAgent } from './verifier-agent.js';
|
|
31
|
+
export { DeepAnalyzer } from './deep-analyzer.js';
|
|
32
|
+
export { ScoringEngine, GRADES, CATEGORIES } from './scoring-engine.js';
|
|
33
|
+
export { SBOMGenerator } from './sbom-generator.js';
|
|
34
|
+
export { PolicyEngine } from './policy-engine.js';
|
|
35
|
+
export { HTMLReporter } from './html-reporter.js';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Create a fully configured orchestrator with all 16 scanning agents.
|
|
39
|
+
* (VerifierAgent and DeepAnalyzer run as post-processors, not in the agent pool.)
|
|
40
|
+
*/
|
|
41
|
+
import { Orchestrator as OrchestratorClass } from './orchestrator.js';
|
|
42
|
+
import { InjectionTester as InjectionTesterClass } from './injection-tester.js';
|
|
43
|
+
import { AuthBypassAgent as AuthBypassAgentClass } from './auth-bypass-agent.js';
|
|
44
|
+
import { SSRFProber as SSRFProberClass } from './ssrf-prober.js';
|
|
45
|
+
import { SupplyChainAudit as SupplyChainAuditClass } from './supply-chain-agent.js';
|
|
46
|
+
import { ConfigAuditor as ConfigAuditorClass } from './config-auditor.js';
|
|
47
|
+
import { LLMRedTeam as LLMRedTeamClass } from './llm-redteam.js';
|
|
48
|
+
import { MobileScanner as MobileScannerClass } from './mobile-scanner.js';
|
|
49
|
+
import { GitHistoryScanner as GitHistoryScannerClass } from './git-history-scanner.js';
|
|
50
|
+
import { CICDScanner as CICDScannerClass } from './cicd-scanner.js';
|
|
51
|
+
import { APIFuzzer as APIFuzzerClass } from './api-fuzzer.js';
|
|
52
|
+
import { SupabaseRLSAgent as SupabaseRLSAgentClass } from './supabase-rls-agent.js';
|
|
53
|
+
import { MCPSecurityAgent as MCPSecurityAgentClass } from './mcp-security-agent.js';
|
|
54
|
+
import { AgenticSecurityAgent as AgenticSecurityAgentClass } from './agentic-security-agent.js';
|
|
55
|
+
import { RAGSecurityAgent as RAGSecurityAgentClass } from './rag-security-agent.js';
|
|
56
|
+
import { PIIComplianceAgent as PIIComplianceAgentClass } from './pii-compliance-agent.js';
|
|
57
|
+
import { VibeCodingAgent as VibeCodingAgentClass } from './vibe-coding-agent.js';
|
|
58
|
+
import { ExceptionHandlerAgent as ExceptionHandlerAgentClass } from './exception-handler-agent.js';
|
|
59
|
+
import { AgentConfigScanner as AgentConfigScannerClass } from './agent-config-scanner.js';
|
|
60
|
+
|
|
61
|
+
export function buildOrchestrator() {
|
|
62
|
+
const orchestrator = new OrchestratorClass();
|
|
63
|
+
orchestrator.registerAll([
|
|
64
|
+
new InjectionTesterClass(),
|
|
65
|
+
new AuthBypassAgentClass(),
|
|
66
|
+
new SSRFProberClass(),
|
|
67
|
+
new SupplyChainAuditClass(),
|
|
68
|
+
new ConfigAuditorClass(),
|
|
69
|
+
new LLMRedTeamClass(),
|
|
70
|
+
new MobileScannerClass(),
|
|
71
|
+
new GitHistoryScannerClass(),
|
|
72
|
+
new CICDScannerClass(),
|
|
73
|
+
new APIFuzzerClass(),
|
|
74
|
+
new SupabaseRLSAgentClass(),
|
|
75
|
+
new MCPSecurityAgentClass(),
|
|
76
|
+
new AgenticSecurityAgentClass(),
|
|
77
|
+
new RAGSecurityAgentClass(),
|
|
78
|
+
new PIIComplianceAgentClass(),
|
|
79
|
+
new VibeCodingAgentClass(),
|
|
80
|
+
new ExceptionHandlerAgentClass(),
|
|
81
|
+
new AgentConfigScannerClass(),
|
|
82
|
+
]);
|
|
83
|
+
return orchestrator;
|
|
84
|
+
}
|