jaku.sh 1.0.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/LICENSE +52 -0
- package/README.md +636 -0
- package/action.yml +264 -0
- package/bin/jaku +2 -0
- package/package.json +62 -0
- package/src/agents/ai-agent.js +175 -0
- package/src/agents/api-agent.js +95 -0
- package/src/agents/base-agent.js +158 -0
- package/src/agents/crawl-agent.js +175 -0
- package/src/agents/event-bus.js +59 -0
- package/src/agents/findings-ledger.js +410 -0
- package/src/agents/logic-agent.js +144 -0
- package/src/agents/orchestrator.js +323 -0
- package/src/agents/qa-agent.js +149 -0
- package/src/agents/security-agent.js +211 -0
- package/src/cli.js +423 -0
- package/src/core/accessibility-checker.js +171 -0
- package/src/core/ai/ai-endpoint-detector.js +227 -0
- package/src/core/ai/guardrail-prober.js +362 -0
- package/src/core/ai/indirect-injector.js +106 -0
- package/src/core/ai/jailbreak-tester.js +212 -0
- package/src/core/ai/model-dos-tester.js +174 -0
- package/src/core/ai/model-fingerprinter.js +246 -0
- package/src/core/ai/multi-turn-attacker.js +297 -0
- package/src/core/ai/output-analyzer.js +182 -0
- package/src/core/ai/prompt-injector.js +543 -0
- package/src/core/ai/system-prompt-extractor.js +244 -0
- package/src/core/api/api-key-auditor.js +266 -0
- package/src/core/api/auth-flow-tester.js +430 -0
- package/src/core/api/cors-ws-tester.js +263 -0
- package/src/core/api/graphql-tester.js +287 -0
- package/src/core/api/oauth-prober.js +343 -0
- package/src/core/auth-manager.js +902 -0
- package/src/core/broken-flow-detector.js +207 -0
- package/src/core/browser-manager.js +119 -0
- package/src/core/console-monitor.js +111 -0
- package/src/core/crawler.js +430 -0
- package/src/core/csr-waiter.js +410 -0
- package/src/core/form-validator.js +240 -0
- package/src/core/logic/abuse-pattern-scanner.js +291 -0
- package/src/core/logic/access-boundary-tester.js +448 -0
- package/src/core/logic/business-rule-inferrer.js +196 -0
- package/src/core/logic/graphql-auditor.js +298 -0
- package/src/core/logic/parameter-polluter.js +212 -0
- package/src/core/logic/pricing-exploiter.js +299 -0
- package/src/core/logic/race-condition-detector.js +222 -0
- package/src/core/logic/workflow-enforcer.js +284 -0
- package/src/core/performance-checker.js +204 -0
- package/src/core/responsive-checker.js +228 -0
- package/src/core/security/cors-prober.js +150 -0
- package/src/core/security/csrf-prober.js +217 -0
- package/src/core/security/dependency-auditor.js +182 -0
- package/src/core/security/file-upload-tester.js +340 -0
- package/src/core/security/header-analyzer.js +324 -0
- package/src/core/security/infra-scanner.js +391 -0
- package/src/core/security/path-traversal.js +112 -0
- package/src/core/security/prototype-pollution.js +147 -0
- package/src/core/security/secret-detector.js +517 -0
- package/src/core/security/sqli-prober.js +257 -0
- package/src/core/security/tls-checker.js +223 -0
- package/src/core/security/xss-scanner.js +225 -0
- package/src/core/test-generator.js +339 -0
- package/src/core/test-runner.js +398 -0
- package/src/reporting/diff-reporter.js +172 -0
- package/src/reporting/report-generator.js +408 -0
- package/src/reporting/sarif-generator.js +190 -0
- package/src/utils/config.js +57 -0
- package/src/utils/finding.js +67 -0
- package/src/utils/logger.js +50 -0
package/action.yml
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
name: 'JAKU Security Scan'
|
|
2
|
+
description: 'Run JAKU autonomous security & QA scanning on your web application'
|
|
3
|
+
author: 'JAKU'
|
|
4
|
+
branding:
|
|
5
|
+
icon: 'shield'
|
|
6
|
+
color: 'green'
|
|
7
|
+
|
|
8
|
+
inputs:
|
|
9
|
+
target-url:
|
|
10
|
+
description: 'Target URL to scan'
|
|
11
|
+
required: true
|
|
12
|
+
config-path:
|
|
13
|
+
description: 'Path to jaku.config.json'
|
|
14
|
+
required: false
|
|
15
|
+
default: ''
|
|
16
|
+
modules:
|
|
17
|
+
description: 'Comma-separated modules to run (qa,security,ai,logic,api)'
|
|
18
|
+
required: false
|
|
19
|
+
default: 'qa,security,ai,logic,api'
|
|
20
|
+
severity-threshold:
|
|
21
|
+
description: 'Minimum severity to report (critical|high|medium|low)'
|
|
22
|
+
required: false
|
|
23
|
+
default: 'low'
|
|
24
|
+
fail-on-severity:
|
|
25
|
+
description: 'Fail the action if findings at this severity or above are found (critical|high|medium|low|none)'
|
|
26
|
+
required: false
|
|
27
|
+
default: 'high'
|
|
28
|
+
sarif-upload:
|
|
29
|
+
description: 'Upload SARIF results to GitHub Security tab'
|
|
30
|
+
required: false
|
|
31
|
+
default: 'true'
|
|
32
|
+
comment-on-pr:
|
|
33
|
+
description: 'Post scan summary as PR comment'
|
|
34
|
+
required: false
|
|
35
|
+
default: 'true'
|
|
36
|
+
auth-username:
|
|
37
|
+
description: 'Username for authenticated scanning'
|
|
38
|
+
required: false
|
|
39
|
+
default: ''
|
|
40
|
+
auth-password:
|
|
41
|
+
description: 'Password for authenticated scanning'
|
|
42
|
+
required: false
|
|
43
|
+
default: ''
|
|
44
|
+
auth-strategy:
|
|
45
|
+
description: 'Authentication strategy (auto|form|api|cookie)'
|
|
46
|
+
required: false
|
|
47
|
+
default: 'auto'
|
|
48
|
+
max-pages:
|
|
49
|
+
description: 'Maximum pages to crawl'
|
|
50
|
+
required: false
|
|
51
|
+
default: '50'
|
|
52
|
+
verbose:
|
|
53
|
+
description: 'Enable verbose logging'
|
|
54
|
+
required: false
|
|
55
|
+
default: 'false'
|
|
56
|
+
|
|
57
|
+
outputs:
|
|
58
|
+
report-path:
|
|
59
|
+
description: 'Path to the generated report directory'
|
|
60
|
+
sarif-path:
|
|
61
|
+
description: 'Path to the SARIF report file'
|
|
62
|
+
findings-count:
|
|
63
|
+
description: 'Total number of findings'
|
|
64
|
+
critical-count:
|
|
65
|
+
description: 'Number of critical findings'
|
|
66
|
+
high-count:
|
|
67
|
+
description: 'Number of high findings'
|
|
68
|
+
exit-code:
|
|
69
|
+
description: 'Exit code (0 = pass, 1 = findings above threshold)'
|
|
70
|
+
|
|
71
|
+
runs:
|
|
72
|
+
using: 'composite'
|
|
73
|
+
steps:
|
|
74
|
+
- name: Setup Node.js
|
|
75
|
+
uses: actions/setup-node@v4
|
|
76
|
+
with:
|
|
77
|
+
node-version: '20'
|
|
78
|
+
|
|
79
|
+
- name: Install JAKU
|
|
80
|
+
shell: bash
|
|
81
|
+
run: |
|
|
82
|
+
cd ${{ github.action_path }}
|
|
83
|
+
npm ci --production 2>/dev/null || npm install --production
|
|
84
|
+
|
|
85
|
+
- name: Install Playwright browsers
|
|
86
|
+
shell: bash
|
|
87
|
+
run: |
|
|
88
|
+
cd ${{ github.action_path }}
|
|
89
|
+
npx playwright install chromium --with-deps
|
|
90
|
+
|
|
91
|
+
- name: Run JAKU Scan
|
|
92
|
+
id: scan
|
|
93
|
+
shell: bash
|
|
94
|
+
env:
|
|
95
|
+
JAKU_TARGET_URL: ${{ inputs.target-url }}
|
|
96
|
+
JAKU_MODULES: ${{ inputs.modules }}
|
|
97
|
+
JAKU_SEVERITY: ${{ inputs.severity-threshold }}
|
|
98
|
+
JAKU_FAIL_ON: ${{ inputs.fail-on-severity }}
|
|
99
|
+
JAKU_CONFIG: ${{ inputs.config-path }}
|
|
100
|
+
JAKU_AUTH_USER: ${{ inputs.auth-username }}
|
|
101
|
+
JAKU_AUTH_PASS: ${{ inputs.auth-password }}
|
|
102
|
+
JAKU_AUTH_STRATEGY: ${{ inputs.auth-strategy }}
|
|
103
|
+
JAKU_MAX_PAGES: ${{ inputs.max-pages }}
|
|
104
|
+
JAKU_VERBOSE: ${{ inputs.verbose }}
|
|
105
|
+
run: |
|
|
106
|
+
REPORT_DIR="${{ runner.temp }}/jaku-reports"
|
|
107
|
+
|
|
108
|
+
# Build command
|
|
109
|
+
CMD="node ${{ github.action_path }}/src/cli.js scan \"${JAKU_TARGET_URL}\""
|
|
110
|
+
CMD="${CMD} -m ${JAKU_MODULES}"
|
|
111
|
+
CMD="${CMD} -s ${JAKU_SEVERITY}"
|
|
112
|
+
CMD="${CMD} -o ${REPORT_DIR}"
|
|
113
|
+
CMD="${CMD} --max-pages ${JAKU_MAX_PAGES}"
|
|
114
|
+
CMD="${CMD} --prod-safe"
|
|
115
|
+
|
|
116
|
+
if [ -n "${JAKU_CONFIG}" ]; then
|
|
117
|
+
CMD="${CMD} -c ${JAKU_CONFIG}"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
if [ -n "${JAKU_AUTH_USER}" ] && [ -n "${JAKU_AUTH_PASS}" ]; then
|
|
121
|
+
CMD="${CMD} --username ${JAKU_AUTH_USER} --password ${JAKU_AUTH_PASS} --auth-strategy ${JAKU_AUTH_STRATEGY}"
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
if [ "${JAKU_VERBOSE}" = "true" ]; then
|
|
125
|
+
CMD="${CMD} --verbose"
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Run scan
|
|
129
|
+
eval ${CMD} || true
|
|
130
|
+
|
|
131
|
+
# Parse results
|
|
132
|
+
if [ -f "${REPORT_DIR}/report.json" ]; then
|
|
133
|
+
eval $(node --input-type=module -e "
|
|
134
|
+
import { readFileSync } from 'fs';
|
|
135
|
+
const r = JSON.parse(readFileSync('${REPORT_DIR}/report.json', 'utf-8'));
|
|
136
|
+
const s = r.dedupSummary || r.summary;
|
|
137
|
+
console.log('FINDINGS=' + s.total);
|
|
138
|
+
console.log('CRITICAL=' + s.critical);
|
|
139
|
+
console.log('HIGH=' + s.high);
|
|
140
|
+
console.log('MEDIUM=' + s.medium);
|
|
141
|
+
console.log('LOW=' + s.low);
|
|
142
|
+
")
|
|
143
|
+
else
|
|
144
|
+
FINDINGS=0; CRITICAL=0; HIGH=0; MEDIUM=0; LOW=0
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
echo "report-path=${REPORT_DIR}" >> $GITHUB_OUTPUT
|
|
148
|
+
echo "sarif-path=${REPORT_DIR}/report.sarif" >> $GITHUB_OUTPUT
|
|
149
|
+
echo "findings-count=${FINDINGS}" >> $GITHUB_OUTPUT
|
|
150
|
+
echo "critical-count=${CRITICAL}" >> $GITHUB_OUTPUT
|
|
151
|
+
echo "high-count=${HIGH}" >> $GITHUB_OUTPUT
|
|
152
|
+
|
|
153
|
+
# Determine exit code based on fail-on-severity
|
|
154
|
+
EXIT_CODE=0
|
|
155
|
+
case "${JAKU_FAIL_ON}" in
|
|
156
|
+
critical) [ "${CRITICAL}" -gt 0 ] && EXIT_CODE=1 ;;
|
|
157
|
+
high) [ "${CRITICAL}" -gt 0 ] || [ "${HIGH}" -gt 0 ] && EXIT_CODE=1 ;;
|
|
158
|
+
medium) [ "${CRITICAL}" -gt 0 ] || [ "${HIGH}" -gt 0 ] || [ "${MEDIUM}" -gt 0 ] && EXIT_CODE=1 ;;
|
|
159
|
+
low) [ "${FINDINGS}" -gt 0 ] && EXIT_CODE=1 ;;
|
|
160
|
+
none) EXIT_CODE=0 ;;
|
|
161
|
+
esac
|
|
162
|
+
|
|
163
|
+
echo "exit-code=${EXIT_CODE}" >> $GITHUB_OUTPUT
|
|
164
|
+
|
|
165
|
+
- name: Upload SARIF to GitHub Security
|
|
166
|
+
if: inputs.sarif-upload == 'true' && hashFiles(steps.scan.outputs.sarif-path) != ''
|
|
167
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
168
|
+
with:
|
|
169
|
+
sarif_file: ${{ steps.scan.outputs.sarif-path }}
|
|
170
|
+
category: 'jaku-security-scan'
|
|
171
|
+
continue-on-error: true
|
|
172
|
+
|
|
173
|
+
- name: Comment on PR
|
|
174
|
+
if: inputs.comment-on-pr == 'true' && github.event_name == 'pull_request'
|
|
175
|
+
uses: actions/github-script@v7
|
|
176
|
+
with:
|
|
177
|
+
script: |
|
|
178
|
+
const fs = require('fs');
|
|
179
|
+
const reportPath = '${{ steps.scan.outputs.report-path }}/report.json';
|
|
180
|
+
|
|
181
|
+
let body = '## 🛡️ JAKU Security Scan Results\n\n';
|
|
182
|
+
|
|
183
|
+
if (fs.existsSync(reportPath)) {
|
|
184
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
|
|
185
|
+
const s = report.dedupSummary || report.summary;
|
|
186
|
+
const ds = report.dedupStats;
|
|
187
|
+
|
|
188
|
+
// Status badge
|
|
189
|
+
const hasIssues = s.critical > 0 || s.high > 0;
|
|
190
|
+
body += hasIssues
|
|
191
|
+
? '> ⚠️ **Security issues detected** — review required\n\n'
|
|
192
|
+
: '> ✅ **No critical or high severity issues found**\n\n';
|
|
193
|
+
|
|
194
|
+
// Summary table
|
|
195
|
+
body += '| Severity | Count |\n|----------|-------|\n';
|
|
196
|
+
if (s.critical > 0) body += `| 🔴 Critical | ${s.critical} |\n`;
|
|
197
|
+
if (s.high > 0) body += `| 🟠 High | ${s.high} |\n`;
|
|
198
|
+
if (s.medium > 0) body += `| 🟡 Medium | ${s.medium} |\n`;
|
|
199
|
+
if (s.low > 0) body += `| 🔵 Low | ${s.low} |\n`;
|
|
200
|
+
body += `| **Total** | **${s.total}** |\n\n`;
|
|
201
|
+
|
|
202
|
+
// Dedup stats
|
|
203
|
+
if (ds && ds.duplicatesRemoved > 0) {
|
|
204
|
+
body += `*${ds.rawCount} raw findings deduplicated to ${ds.dedupedCount} unique (${ds.reductionPercent}% reduction)*\n\n`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Top findings
|
|
208
|
+
const findings = report.findings || [];
|
|
209
|
+
if (findings.length > 0) {
|
|
210
|
+
body += '### Top Findings\n\n';
|
|
211
|
+
for (const f of findings.slice(0, 5)) {
|
|
212
|
+
const icon = { critical: '🔴', high: '🟠', medium: '🟡', low: '🔵', info: '⚪' }[f.severity] || '⚪';
|
|
213
|
+
body += `- ${icon} **${f.title}** — ${f.affected_surface || f.affected_surfaces?.[0] || 'N/A'}\n`;
|
|
214
|
+
}
|
|
215
|
+
if (findings.length > 5) {
|
|
216
|
+
body += `\n*...and ${findings.length - 5} more. See full report in artifacts.*\n`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
body += '\n---\n*Scanned by [JAKU](https://github.com/jaku-security/jaku) v1.0.0*';
|
|
221
|
+
} else {
|
|
222
|
+
body += '⚠️ Scan completed but no report was generated. Check workflow logs for errors.';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Find and update existing comment, or create new one
|
|
226
|
+
const { data: comments } = await github.rest.issues.listComments({
|
|
227
|
+
owner: context.repo.owner,
|
|
228
|
+
repo: context.repo.repo,
|
|
229
|
+
issue_number: context.issue.number,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const botComment = comments.find(c => c.body.includes('JAKU Security Scan Results'));
|
|
233
|
+
|
|
234
|
+
if (botComment) {
|
|
235
|
+
await github.rest.issues.updateComment({
|
|
236
|
+
owner: context.repo.owner,
|
|
237
|
+
repo: context.repo.repo,
|
|
238
|
+
comment_id: botComment.id,
|
|
239
|
+
body,
|
|
240
|
+
});
|
|
241
|
+
} else {
|
|
242
|
+
await github.rest.issues.createComment({
|
|
243
|
+
owner: context.repo.owner,
|
|
244
|
+
repo: context.repo.repo,
|
|
245
|
+
issue_number: context.issue.number,
|
|
246
|
+
body,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
- name: Upload Report Artifacts
|
|
251
|
+
if: always()
|
|
252
|
+
uses: actions/upload-artifact@v4
|
|
253
|
+
with:
|
|
254
|
+
name: jaku-scan-report
|
|
255
|
+
path: ${{ steps.scan.outputs.report-path }}
|
|
256
|
+
retention-days: 30
|
|
257
|
+
|
|
258
|
+
- name: Check Threshold
|
|
259
|
+
if: steps.scan.outputs.exit-code == '1'
|
|
260
|
+
shell: bash
|
|
261
|
+
run: |
|
|
262
|
+
echo "::error::JAKU scan found findings at or above the '${{ inputs.fail-on-severity }}' severity threshold."
|
|
263
|
+
echo "Critical: ${{ steps.scan.outputs.critical-count }}, High: ${{ steps.scan.outputs.high-count }}, Total: ${{ steps.scan.outputs.findings-count }}"
|
|
264
|
+
exit 1
|
package/bin/jaku
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jaku.sh",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "JAKU (呪) — Autonomous Security & Quality Intelligence Agent for vibe-coded apps. XSS, SQLi, prompt injection, QA testing, and attack chain correlation in one command.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"jaku": "./bin/jaku"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src/",
|
|
15
|
+
"bin/",
|
|
16
|
+
"action.yml",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"scan": "node src/cli.js scan",
|
|
21
|
+
"prepublishOnly": "node src/cli.js --help"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"security",
|
|
25
|
+
"qa",
|
|
26
|
+
"testing",
|
|
27
|
+
"automation",
|
|
28
|
+
"playwright",
|
|
29
|
+
"scanning",
|
|
30
|
+
"xss",
|
|
31
|
+
"sql-injection",
|
|
32
|
+
"prompt-injection",
|
|
33
|
+
"ai-security",
|
|
34
|
+
"vulnerability-scanner",
|
|
35
|
+
"penetration-testing",
|
|
36
|
+
"vibe-coding",
|
|
37
|
+
"autonomous-testing",
|
|
38
|
+
"attack-chain"
|
|
39
|
+
],
|
|
40
|
+
"author": {
|
|
41
|
+
"name": "Shantanu Pandey",
|
|
42
|
+
"url": "https://github.com/theshantanupandey"
|
|
43
|
+
},
|
|
44
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
45
|
+
"homepage": "https://jakusec.dev",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/theshantanupandey/jaku.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/theshantanupandey/jaku/issues"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"chalk": "^5.3.0",
|
|
55
|
+
"commander": "^12.1.0",
|
|
56
|
+
"nanoid": "^5.0.9",
|
|
57
|
+
"ora": "^8.1.1",
|
|
58
|
+
"p-limit": "^7.3.0",
|
|
59
|
+
"playwright": "^1.49.1",
|
|
60
|
+
"winston": "^3.17.0"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { BaseAgent } from './base-agent.js';
|
|
2
|
+
import { AIEndpointDetector } from '../core/ai/ai-endpoint-detector.js';
|
|
3
|
+
import { PromptInjector } from '../core/ai/prompt-injector.js';
|
|
4
|
+
import { JailbreakTester } from '../core/ai/jailbreak-tester.js';
|
|
5
|
+
import { SystemPromptExtractor } from '../core/ai/system-prompt-extractor.js';
|
|
6
|
+
import { OutputAnalyzer } from '../core/ai/output-analyzer.js';
|
|
7
|
+
import { GuardrailProber } from '../core/ai/guardrail-prober.js';
|
|
8
|
+
import { ModelDoSTester } from '../core/ai/model-dos-tester.js';
|
|
9
|
+
import { IndirectInjector } from '../core/ai/indirect-injector.js';
|
|
10
|
+
import { MultiTurnAttacker } from '../core/ai/multi-turn-attacker.js';
|
|
11
|
+
import { ModelFingerprinter } from '../core/ai/model-fingerprinter.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* JAKU-AI — Prompt Injection & AI Abuse Detection Agent
|
|
15
|
+
*
|
|
16
|
+
* Pipeline:
|
|
17
|
+
* 1. Detect AI endpoints (auto-discovery from surface inventory)
|
|
18
|
+
* 2. Prompt Injection testing (many-shot, encoding, delimiter, context flood, RAG, CoT)
|
|
19
|
+
* 3. Jailbreak testing (DAN, AIM, model-specific token attacks, persona anchoring)
|
|
20
|
+
* 4. System Prompt Extraction (17 techniques)
|
|
21
|
+
* 5. Output Analysis (AI-mediated XSS, markdown rendering attacks)
|
|
22
|
+
* 6. Guardrail Probing (PII, agency, tool abuse, SSRF, agentic tool injection)
|
|
23
|
+
* 7. Model DoS Testing (context bombing, token loops)
|
|
24
|
+
* 8. Indirect Injection Testing (6 embedded payloads)
|
|
25
|
+
* 9. Multi-Turn Attack Testing (trust escalation, context drift, memory poisoning)
|
|
26
|
+
* 10. Model Fingerprinting + Model-Specific Exploits
|
|
27
|
+
*
|
|
28
|
+
* Dependencies: JAKU-CRAWL (runs in Wave 2, parallel with QA + SEC)
|
|
29
|
+
*/
|
|
30
|
+
export class AIAgent extends BaseAgent {
|
|
31
|
+
get name() { return 'JAKU-AI'; }
|
|
32
|
+
get dependencies() { return ['JAKU-CRAWL']; }
|
|
33
|
+
|
|
34
|
+
async _execute(context) {
|
|
35
|
+
const { config, logger, surfaceInventory } = context;
|
|
36
|
+
|
|
37
|
+
if (!surfaceInventory) {
|
|
38
|
+
throw new Error('No surface inventory available — JAKU-CRAWL must run first');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Phase 1: Detect AI endpoints
|
|
42
|
+
this.progress('detect', 'Detecting AI-powered endpoints...', 0);
|
|
43
|
+
|
|
44
|
+
const detector = new AIEndpointDetector(logger);
|
|
45
|
+
const aiSurfaces = await detector.detect(surfaceInventory);
|
|
46
|
+
|
|
47
|
+
this._log(`Detected ${aiSurfaces.length} AI surfaces (${aiSurfaces.filter(s => s.confidence === 'confirmed').length} confirmed)`);
|
|
48
|
+
this.progress('detect', `Found ${aiSurfaces.length} AI endpoints`, 10);
|
|
49
|
+
|
|
50
|
+
if (aiSurfaces.length === 0) {
|
|
51
|
+
this._log('No AI endpoints detected — skipping AI abuse tests');
|
|
52
|
+
this.progress('complete', 'No AI endpoints found — scan skipped', 100);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Create shared sendMessage function for sub-modules
|
|
57
|
+
const injector = new PromptInjector(logger);
|
|
58
|
+
const sendMessage = injector._sendMessage.bind(injector);
|
|
59
|
+
|
|
60
|
+
// Phase 2: Prompt Injection
|
|
61
|
+
this.progress('injection', 'Testing for prompt injection...', 15);
|
|
62
|
+
try {
|
|
63
|
+
const injectionFindings = await injector.inject(aiSurfaces);
|
|
64
|
+
this.addFindings(injectionFindings);
|
|
65
|
+
this._log(`Prompt injection: ${injectionFindings.length} vulnerabilities`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
this._log(`Prompt injection testing failed: ${err.message}`, 'error');
|
|
68
|
+
}
|
|
69
|
+
this.progress('injection', 'Prompt injection testing complete', 30);
|
|
70
|
+
|
|
71
|
+
// Phase 3: Jailbreak Testing
|
|
72
|
+
this.progress('jailbreak', 'Testing jailbreak techniques...', 30);
|
|
73
|
+
try {
|
|
74
|
+
const tester = new JailbreakTester(logger);
|
|
75
|
+
const jailbreakFindings = await tester.test(aiSurfaces, sendMessage);
|
|
76
|
+
this.addFindings(jailbreakFindings);
|
|
77
|
+
this._log(`Jailbreak: ${jailbreakFindings.length} vulnerabilities`);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
this._log(`Jailbreak testing failed: ${err.message}`, 'error');
|
|
80
|
+
}
|
|
81
|
+
this.progress('jailbreak', 'Jailbreak testing complete', 50);
|
|
82
|
+
|
|
83
|
+
// Phase 4: System Prompt Extraction
|
|
84
|
+
this.progress('extraction', 'Attempting system prompt extraction...', 50);
|
|
85
|
+
try {
|
|
86
|
+
const extractor = new SystemPromptExtractor(logger);
|
|
87
|
+
const extractionFindings = await extractor.extract(aiSurfaces, sendMessage);
|
|
88
|
+
this.addFindings(extractionFindings);
|
|
89
|
+
this._log(`System prompt extraction: ${extractionFindings.length} leaks`);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
this._log(`System prompt extraction failed: ${err.message}`, 'error');
|
|
92
|
+
}
|
|
93
|
+
this.progress('extraction', 'System prompt extraction complete', 70);
|
|
94
|
+
|
|
95
|
+
// Phase 5: Output Analysis (AI-mediated XSS)
|
|
96
|
+
this.progress('output', 'Analyzing AI output sanitization...', 70);
|
|
97
|
+
try {
|
|
98
|
+
const analyzer = new OutputAnalyzer(logger);
|
|
99
|
+
const outputFindings = await analyzer.analyze(aiSurfaces, sendMessage);
|
|
100
|
+
this.addFindings(outputFindings);
|
|
101
|
+
this._log(`Output analysis: ${outputFindings.length} issues`);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
this._log(`Output analysis failed: ${err.message}`, 'error');
|
|
104
|
+
}
|
|
105
|
+
this.progress('output', 'Output analysis complete', 85);
|
|
106
|
+
|
|
107
|
+
// Phase 6: Guardrail Probing
|
|
108
|
+
this.progress('guardrails', 'Probing AI guardrails...', 85);
|
|
109
|
+
try {
|
|
110
|
+
const prober = new GuardrailProber(logger);
|
|
111
|
+
const guardrailFindings = await prober.probe(aiSurfaces, sendMessage);
|
|
112
|
+
this.addFindings(guardrailFindings);
|
|
113
|
+
this._log(`Guardrails: ${guardrailFindings.length} bypasses`);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
this._log(`Guardrail probing failed: ${err.message}`, 'error');
|
|
116
|
+
}
|
|
117
|
+
this.progress('guardrails', 'Guardrail probing complete', 80);
|
|
118
|
+
|
|
119
|
+
// Phase 7: Model DoS
|
|
120
|
+
this.progress('dos', 'Testing model DoS vectors...', 80);
|
|
121
|
+
try {
|
|
122
|
+
const dosTester = new ModelDoSTester(logger);
|
|
123
|
+
const dosFindings = await dosTester.test(aiSurfaces, sendMessage);
|
|
124
|
+
this.addFindings(dosFindings);
|
|
125
|
+
this._log(`Model DoS: ${dosFindings.length} issues`);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
this._log(`Model DoS testing failed: ${err.message}`, 'error');
|
|
128
|
+
}
|
|
129
|
+
this.progress('dos', 'Model DoS testing complete', 90);
|
|
130
|
+
|
|
131
|
+
// Phase 8: Indirect Injection
|
|
132
|
+
this.progress('indirect', 'Testing indirect prompt injection...', 90);
|
|
133
|
+
try {
|
|
134
|
+
const indirectInjector = new IndirectInjector(logger);
|
|
135
|
+
const indirectFindings = await indirectInjector.test(aiSurfaces, sendMessage);
|
|
136
|
+
this.addFindings(indirectFindings);
|
|
137
|
+
this._log(`Indirect injection: ${indirectFindings.length} vulnerabilities`);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
this._log(`Indirect injection testing failed: ${err.message}`, 'error');
|
|
140
|
+
}
|
|
141
|
+
this.progress('indirect', 'Indirect injection testing complete', 95);
|
|
142
|
+
|
|
143
|
+
// Phase 9: Multi-Turn Attack Testing
|
|
144
|
+
this.progress('multiturn', 'Running multi-turn attack scenarios...', 95);
|
|
145
|
+
try {
|
|
146
|
+
const injector = new PromptInjector(logger);
|
|
147
|
+
const sendMsg = injector._sendMessage.bind(injector);
|
|
148
|
+
const multiTurnAttacker = new MultiTurnAttacker(logger);
|
|
149
|
+
const multiTurnFindings = await multiTurnAttacker.test(aiSurfaces, sendMsg);
|
|
150
|
+
this.addFindings(multiTurnFindings);
|
|
151
|
+
this._log(`Multi-turn attacks: ${multiTurnFindings.length} vulnerabilities`);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
this._log(`Multi-turn testing failed: ${err.message}`, 'error');
|
|
154
|
+
}
|
|
155
|
+
this.progress('multiturn', 'Multi-turn attack testing complete', 97);
|
|
156
|
+
|
|
157
|
+
// Phase 10: Model Fingerprinting + Model-Specific Exploits
|
|
158
|
+
this.progress('fingerprint', 'Fingerprinting model and running model-specific attacks...', 97);
|
|
159
|
+
try {
|
|
160
|
+
const injector2 = new PromptInjector(logger);
|
|
161
|
+
const sendMsg2 = injector2._sendMessage.bind(injector2);
|
|
162
|
+
const fingerprinter = new ModelFingerprinter(logger);
|
|
163
|
+
const fingerprintFindings = await fingerprinter.test(aiSurfaces, sendMsg2);
|
|
164
|
+
this.addFindings(fingerprintFindings);
|
|
165
|
+
this._log(`Model fingerprinting: ${fingerprintFindings.length} findings`);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
this._log(`Model fingerprinting failed: ${err.message}`, 'error');
|
|
168
|
+
}
|
|
169
|
+
this.progress('fingerprint', 'Model fingerprinting complete', 100);
|
|
170
|
+
|
|
171
|
+
this.progress('complete', `AI scan complete — ${this._findings.length} total findings`, 100);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export default AIAgent;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { BaseAgent } from './base-agent.js';
|
|
2
|
+
import { AuthFlowTester } from '../core/api/auth-flow-tester.js';
|
|
3
|
+
import { OAuthProber } from '../core/api/oauth-prober.js';
|
|
4
|
+
import { APIKeyAuditor } from '../core/api/api-key-auditor.js';
|
|
5
|
+
import { GraphQLTester } from '../core/api/graphql-tester.js';
|
|
6
|
+
import { CORSWSTester } from '../core/api/cors-ws-tester.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* JAKU-API — API & Auth Flow Verification Agent
|
|
10
|
+
*
|
|
11
|
+
* Pipeline:
|
|
12
|
+
* 1. Test authentication flows (JWT, sessions, passwords, MFA)
|
|
13
|
+
* 2. Probe OAuth/SSO security
|
|
14
|
+
* 3. Audit API key management
|
|
15
|
+
* 4. Test GraphQL-specific vulnerabilities
|
|
16
|
+
* 5. Validate CORS and WebSocket security
|
|
17
|
+
*
|
|
18
|
+
* Dependencies: JAKU-CRAWL (runs in Wave 2, parallel with QA + SEC + AI + LOGIC)
|
|
19
|
+
*/
|
|
20
|
+
export class APIAgent extends BaseAgent {
|
|
21
|
+
get name() { return 'JAKU-API'; }
|
|
22
|
+
get dependencies() { return ['JAKU-CRAWL']; }
|
|
23
|
+
|
|
24
|
+
async _execute(context) {
|
|
25
|
+
const { config, logger, surfaceInventory } = context;
|
|
26
|
+
|
|
27
|
+
if (!surfaceInventory) {
|
|
28
|
+
throw new Error('No surface inventory available — JAKU-CRAWL must run first');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Phase 1: Auth flow testing
|
|
32
|
+
this.progress('auth', 'Testing authentication flows...', 0);
|
|
33
|
+
try {
|
|
34
|
+
const authTester = new AuthFlowTester(logger);
|
|
35
|
+
const authFindings = await authTester.test(surfaceInventory);
|
|
36
|
+
this.addFindings(authFindings);
|
|
37
|
+
this._log(`Auth flows: ${authFindings.length} issues`);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
this._log(`Auth flow testing failed: ${err.message}`, 'error');
|
|
40
|
+
}
|
|
41
|
+
this.progress('auth', 'Auth flow testing complete', 20);
|
|
42
|
+
|
|
43
|
+
// Phase 2: OAuth probing
|
|
44
|
+
this.progress('oauth', 'Probing OAuth/SSO flows...', 20);
|
|
45
|
+
try {
|
|
46
|
+
const oauthProber = new OAuthProber(logger);
|
|
47
|
+
const oauthFindings = await oauthProber.probe(surfaceInventory);
|
|
48
|
+
this.addFindings(oauthFindings);
|
|
49
|
+
this._log(`OAuth: ${oauthFindings.length} issues`);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
this._log(`OAuth probing failed: ${err.message}`, 'error');
|
|
52
|
+
}
|
|
53
|
+
this.progress('oauth', 'OAuth probing complete', 40);
|
|
54
|
+
|
|
55
|
+
// Phase 3: API key audit
|
|
56
|
+
this.progress('apikeys', 'Auditing API key management...', 40);
|
|
57
|
+
try {
|
|
58
|
+
const keyAuditor = new APIKeyAuditor(logger);
|
|
59
|
+
const keyFindings = await keyAuditor.audit(surfaceInventory);
|
|
60
|
+
this.addFindings(keyFindings);
|
|
61
|
+
this._log(`API keys: ${keyFindings.length} issues`);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
this._log(`API key audit failed: ${err.message}`, 'error');
|
|
64
|
+
}
|
|
65
|
+
this.progress('apikeys', 'API key audit complete', 60);
|
|
66
|
+
|
|
67
|
+
// Phase 4: GraphQL testing
|
|
68
|
+
this.progress('graphql', 'Testing GraphQL endpoints...', 60);
|
|
69
|
+
try {
|
|
70
|
+
const gqlTester = new GraphQLTester(logger);
|
|
71
|
+
const gqlFindings = await gqlTester.test(surfaceInventory);
|
|
72
|
+
this.addFindings(gqlFindings);
|
|
73
|
+
this._log(`GraphQL: ${gqlFindings.length} issues`);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
this._log(`GraphQL testing failed: ${err.message}`, 'error');
|
|
76
|
+
}
|
|
77
|
+
this.progress('graphql', 'GraphQL testing complete', 80);
|
|
78
|
+
|
|
79
|
+
// Phase 5: CORS & WebSocket testing
|
|
80
|
+
this.progress('cors-ws', 'Testing CORS and WebSocket security...', 80);
|
|
81
|
+
try {
|
|
82
|
+
const corsTester = new CORSWSTester(logger);
|
|
83
|
+
const corsFindings = await corsTester.test(surfaceInventory);
|
|
84
|
+
this.addFindings(corsFindings);
|
|
85
|
+
this._log(`CORS/WS: ${corsFindings.length} issues`);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
this._log(`CORS/WS testing failed: ${err.message}`, 'error');
|
|
88
|
+
}
|
|
89
|
+
this.progress('cors-ws', 'CORS/WS testing complete', 100);
|
|
90
|
+
|
|
91
|
+
this.progress('complete', `API scan complete — ${this._findings.length} total findings`, 100);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default APIAgent;
|