praxis-sec 1.0.0 → 1.1.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 +29 -8
- package/cli/agents/ai-infra-inventory-agent.js +5 -1
- package/cli/bin/praxis.js +45 -0
- package/cli/commands/audit.js +44 -8
- package/cli/commands/scan.js +31 -1
- package/cli/core/.praxis/context.json +210 -0
- package/cli/core/.praxis/history.json +186 -0
- package/cli/core/.praxis/llm-cache.json +50 -0
- package/cli/core/ast/guardrail-detector.js +1 -1
- package/cli/core/errors.js +2 -2
- package/cli/core/git-clone.js +200 -0
- package/cli/core/web/.praxis/context.json +135 -0
- package/cli/core/web/.praxis/history.json +93 -0
- package/cli/core/web/.praxis/llm-cache.json +1 -0
- package/cli/core/web/jobs.js +25 -2
- package/cli/core/web/server.js +69 -155
- package/cli/core/web/ui.js +2193 -0
- package/docs/THREAT_INTEL.md +1 -1
- package/docs/USAGE.md +26 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<a href="https://github.com/Ganron007/Praxis/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/Ganron007/Praxis/ci.yml?label=CI" alt="CI"></a>
|
|
9
|
-
<a href="https://github.com/marketplace/actions/praxis-
|
|
9
|
+
<a href="https://github.com/marketplace/actions/praxis-security-scan"><img src="https://img.shields.io/badge/Marketplace-Praxis%20Security%20Scan-blue" alt="GitHub Marketplace"></a>
|
|
10
10
|
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="License: MIT"></a>
|
|
11
11
|
<img src="https://img.shields.io/badge/Node.js-%E2%89%A518.0.0-blue.svg" alt="Node.js: >=18.0.0">
|
|
12
12
|
<img src="https://img.shields.io/npm/v/praxis-sec?label=Version" alt="npm version">
|
|
@@ -40,14 +40,29 @@
|
|
|
40
40
|
| **CI-native** | `scan ci` gates, SARIF for Code Scanning with real `security-severity` ranking, net-new PR gating (fails only on *introduced* findings), GitHub Action inline PR annotations |
|
|
41
41
|
| **Reproducible** | Every scan reports a provenance fingerprint (tool, runtime, probe/threatpack/data versions), and CI enforces determinism between two runs |
|
|
42
42
|
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install -g praxis-sec # then just run: praxis
|
|
47
|
+
npx praxis-sec scan . # or no install at all
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Requires Node.js 18+. Nothing else — no account, no API key. LLM-assisted remediation is
|
|
51
|
+
opt-in via `--deep`.
|
|
52
|
+
|
|
53
|
+
> **The package is `praxis-sec`, but the command is `praxis`.** The unscoped `praxis` name on
|
|
54
|
+
> npm belongs to an unrelated project, so the distribution carries a suffix. Installing it puts
|
|
55
|
+
> a `praxis` executable on your PATH, so every example below reads `praxis …`.
|
|
56
|
+
|
|
43
57
|
## Quick start
|
|
44
58
|
|
|
45
59
|
```bash
|
|
46
|
-
npm install
|
|
60
|
+
npm install -g praxis-sec
|
|
47
61
|
|
|
48
62
|
praxis scan . # full 28-agent audit + AST taint evaluation
|
|
63
|
+
praxis scan git <url> # direct remote Git repo audit (clones to temp dir & audits)
|
|
49
64
|
praxis fix . # interactive LLM-guided fixes
|
|
50
|
-
praxis redteam .
|
|
65
|
+
praxis scan redteam . # adversarial agent pack against a local codebase
|
|
51
66
|
praxis agents audit . # audit the AI/agent surface
|
|
52
67
|
praxis agents mcp --test-live # live MCP JSON-RPC probe
|
|
53
68
|
praxis web # local web UI for scans and findings
|
|
@@ -68,19 +83,25 @@ praxis vibe . # emoji-graded A–F score
|
|
|
68
83
|
## Command groups
|
|
69
84
|
|
|
70
85
|
```
|
|
71
|
-
praxis scan
|
|
86
|
+
praxis scan full · git · secrets · changed · env · redteam · standard · ci
|
|
72
87
|
praxis fix interactive · quick · from-report · rotate · undo · env-template
|
|
73
88
|
praxis agents audit · skill · mcp · bom · serve (MCP server)
|
|
74
89
|
praxis intel update · deps · advisories
|
|
75
90
|
praxis report team · legal · checklist · sbom · benchmark
|
|
76
|
-
praxis project init · doctor · hooks · guard · watch · baseline · plugins · policy
|
|
77
|
-
praxis rules list · export · import
|
|
78
|
-
praxis web local web UI
|
|
91
|
+
praxis project init · doctor · hooks · guard · watch · baseline · memory · playbook · plugins · policy
|
|
92
|
+
praxis rules list · export · import (portable rule bundles)
|
|
93
|
+
praxis web local web UI (read-only, loopback by default)
|
|
79
94
|
|
|
95
|
+
praxis redteam DAST red team against a live LLM endpoint
|
|
96
|
+
praxis hooks Claude Code tool-call security gate
|
|
80
97
|
praxis vibe emoji-graded A–F score
|
|
81
98
|
praxis score numeric score
|
|
82
99
|
```
|
|
83
100
|
|
|
101
|
+
> `praxis redteam` takes a **live endpoint** (e.g. `praxis redteam https://api.example.com`).
|
|
102
|
+
> To run the adversarial agent pack against a **local codebase**, use
|
|
103
|
+
> `praxis scan redteam <path>`.
|
|
104
|
+
|
|
84
105
|
## 28 agents at a glance
|
|
85
106
|
|
|
86
107
|
| Cluster | Agents | Covers |
|
|
@@ -110,7 +131,7 @@ Template: [`.env.example`](.env.example) · Verify with `praxis project doctor`.
|
|
|
110
131
|
## CI
|
|
111
132
|
|
|
112
133
|
```yaml
|
|
113
|
-
- uses: Ganron007/Praxis@
|
|
134
|
+
- uses: Ganron007/Praxis@v1
|
|
114
135
|
with:
|
|
115
136
|
threshold: '80'
|
|
116
137
|
net-new: 'true' # fail only on findings introduced by the PR
|
|
@@ -424,9 +424,13 @@ export class AiInfraInventoryAgent extends BaseAgent {
|
|
|
424
424
|
// Eval-harness / sandbox misconfigurations (P-IMP-048)
|
|
425
425
|
for (const check of EVAL_HARNESS_RISK) {
|
|
426
426
|
if (check.regex.test(content)) {
|
|
427
|
+
const lNum = lineNum(check.regex);
|
|
428
|
+
const lines = content.split('\n');
|
|
429
|
+
const lineText = lines[lNum - 1] || '';
|
|
430
|
+
if (lineText.includes('// praxis-ignore') || lineText.includes('# praxis-ignore')) continue;
|
|
427
431
|
findings.push(createFinding({
|
|
428
432
|
file,
|
|
429
|
-
line:
|
|
433
|
+
line: lNum,
|
|
430
434
|
severity: check.severity,
|
|
431
435
|
category: this.category,
|
|
432
436
|
rule: check.rule,
|
package/cli/bin/praxis.js
CHANGED
|
@@ -150,9 +150,50 @@ scan
|
|
|
150
150
|
.option('--agentic-target <score>', 'Target security score for agentic loop', parseInt)
|
|
151
151
|
.option('--hermes-only', 'Run only Hermes-relevant agents')
|
|
152
152
|
.option('--fail-below <threshold>', 'Exit 1 if score is below threshold')
|
|
153
|
+
.option('-b, --branch <name>', 'Git branch or tag to scan (when target is a Git URL)')
|
|
154
|
+
.option('--depth <n>', 'Git clone depth (default: 1 for shallow clone, 0 for full history)', parseInt)
|
|
155
|
+
.option('--git-token <token>', 'Auth token for private Git repositories (or set PRAXIS_GIT_TOKEN / GITHUB_TOKEN)')
|
|
156
|
+
.option('--git-history', 'Include Git commit history secret scanning')
|
|
157
|
+
.option('--keep-clone', 'Keep cloned repository on disk after scan instead of deleting')
|
|
158
|
+
.option('--submodules', 'Recursively clone Git submodules')
|
|
153
159
|
.option('-v, --verbose', 'Verbose output')
|
|
154
160
|
.action(auditCommand);
|
|
155
161
|
|
|
162
|
+
scan
|
|
163
|
+
.command('git <url>')
|
|
164
|
+
.description('Direct remote Git repository audit: clone to secure temp workspace and scan')
|
|
165
|
+
.option('-b, --branch <name>', 'Git branch or tag to clone and scan')
|
|
166
|
+
.option('--depth <n>', 'Git clone depth (default: 1 for shallow clone)', parseInt)
|
|
167
|
+
.option('--git-token <token>', 'Auth token for private repositories (or set PRAXIS_GIT_TOKEN / GITHUB_TOKEN)')
|
|
168
|
+
.option('--git-history', 'Include Git commit history secret scanning')
|
|
169
|
+
.option('--keep-clone', 'Keep cloned repository on disk after scan')
|
|
170
|
+
.option('--submodules', 'Recursively clone Git submodules')
|
|
171
|
+
.option('--json', 'Output results as JSON')
|
|
172
|
+
.option('--sarif', 'Output results in SARIF format')
|
|
173
|
+
.option('--csv', 'Output results as CSV')
|
|
174
|
+
.option('--md', 'Output results as Markdown')
|
|
175
|
+
.option('--html [file]', 'HTML report path (default: praxis-report.html)')
|
|
176
|
+
.option('--no-deps', 'Skip dependency audit')
|
|
177
|
+
.option('--no-ai', 'Skip AI classification')
|
|
178
|
+
.option('--deep', 'LLM-powered taint analysis for critical/high findings')
|
|
179
|
+
.option('-v, --verbose', 'Verbose output')
|
|
180
|
+
.action((url, opts) => auditCommand(url, opts));
|
|
181
|
+
|
|
182
|
+
scan
|
|
183
|
+
.command('repo <url>')
|
|
184
|
+
.description('Alias for "praxis scan git <url>"')
|
|
185
|
+
.option('-b, --branch <name>', 'Git branch or tag to clone and scan')
|
|
186
|
+
.option('--depth <n>', 'Git clone depth (default: 1)', parseInt)
|
|
187
|
+
.option('--git-token <token>', 'Auth token for private repositories')
|
|
188
|
+
.option('--git-history', 'Include Git commit history secret scanning')
|
|
189
|
+
.option('--keep-clone', 'Keep cloned repository on disk after scan')
|
|
190
|
+
.option('--json', 'Output results as JSON')
|
|
191
|
+
.option('--sarif', 'Output results in SARIF format')
|
|
192
|
+
.option('--html [file]', 'HTML report path (default: praxis-report.html)')
|
|
193
|
+
.option('--no-deps', 'Skip dependency audit')
|
|
194
|
+
.option('-v, --verbose', 'Verbose output')
|
|
195
|
+
.action((url, opts) => auditCommand(url, opts));
|
|
196
|
+
|
|
156
197
|
scan
|
|
157
198
|
.command('secrets [path]')
|
|
158
199
|
.description('Fast pattern-based secret scan only (no agents)')
|
|
@@ -162,6 +203,10 @@ scan
|
|
|
162
203
|
.option('--sarif', 'Output results in SARIF format')
|
|
163
204
|
.option('--include-tests', 'Also scan test files')
|
|
164
205
|
.option('--no-cache', 'Force full rescan')
|
|
206
|
+
.option('-b, --branch <name>', 'Git branch or tag to scan (when target is a Git URL)')
|
|
207
|
+
.option('--depth <n>', 'Git clone depth (default: 1)', parseInt)
|
|
208
|
+
.option('--git-token <token>', 'Auth token for private Git repositories')
|
|
209
|
+
.option('--keep-clone', 'Keep cloned repository on disk after scan')
|
|
165
210
|
.action(scanCommand);
|
|
166
211
|
|
|
167
212
|
scan
|
package/cli/commands/audit.js
CHANGED
|
@@ -45,6 +45,7 @@ import { generatePDF, generatePrintHTML, isChromeAvailable } from '../utils/pdf-
|
|
|
45
45
|
import { SecretsVerifier } from '../utils/secrets-verifier.js';
|
|
46
46
|
import { applyInlineAnnotations } from './autofix.js';
|
|
47
47
|
import { buildScanFingerprint } from '../utils/scan-fingerprint.js';
|
|
48
|
+
import { isGitUrl, cloneGitRepo } from '../core/git-clone.js';
|
|
48
49
|
|
|
49
50
|
// =============================================================================
|
|
50
51
|
// CONSTANTS
|
|
@@ -83,18 +84,44 @@ const EFFORT_MAP = {
|
|
|
83
84
|
// =============================================================================
|
|
84
85
|
|
|
85
86
|
export async function auditCommand(targetPath = '.', options = {}) {
|
|
86
|
-
const absolutePath = path.resolve(targetPath);
|
|
87
87
|
const machineOutput = options.json || options.sarif || options.csv || options.md;
|
|
88
|
+
let gitClone = null;
|
|
89
|
+
let effectivePath = targetPath;
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
// ── Remote Git Repository Support ─────────────────────────────────────────
|
|
92
|
+
if (isGitUrl(targetPath)) {
|
|
93
|
+
if (!machineOutput) printBanner();
|
|
94
|
+
const gitSpinner = machineOutput ? null : ora({ text: chalk.cyan(`Cloning remote Git repository: ${targetPath}...`), color: 'cyan' }).start();
|
|
95
|
+
try {
|
|
96
|
+
gitClone = cloneGitRepo(targetPath, {
|
|
97
|
+
branch: options.branch,
|
|
98
|
+
depth: options.depth,
|
|
99
|
+
gitToken: options.gitToken,
|
|
100
|
+
submodules: options.submodules,
|
|
101
|
+
gitHistory: options.gitHistory,
|
|
102
|
+
});
|
|
103
|
+
effectivePath = gitClone.tempDir;
|
|
104
|
+
if (gitSpinner) gitSpinner.succeed(chalk.green(`Cloned ${gitClone.repoName} (${gitClone.displayUrl}) to temporary workspace`));
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (gitSpinner) gitSpinner.fail(chalk.red(err.message));
|
|
107
|
+
else console.error(chalk.red(err.message));
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
93
111
|
}
|
|
94
112
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
try {
|
|
114
|
+
const absolutePath = path.resolve(effectivePath);
|
|
115
|
+
|
|
116
|
+
if (!fs.existsSync(absolutePath)) {
|
|
117
|
+
console.error(chalk.red(` Path does not exist: ${absolutePath}`));
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!machineOutput && !gitClone) {
|
|
123
|
+
printBanner();
|
|
124
|
+
}
|
|
98
125
|
|
|
99
126
|
// ── Cache Layer ──────────────────────────────────────────────────────────
|
|
100
127
|
// --deep forces a fresh scan so taint analysis covers every finding,
|
|
@@ -624,6 +651,15 @@ export async function auditCommand(targetPath = '.', options = {}) {
|
|
|
624
651
|
}
|
|
625
652
|
process.exitCode = scoreResult.score >= threshold ? 0 : 1;
|
|
626
653
|
}
|
|
654
|
+
} finally {
|
|
655
|
+
if (gitClone) {
|
|
656
|
+
if (!options.keepClone) {
|
|
657
|
+
gitClone.cleanup();
|
|
658
|
+
} else if (!machineOutput) {
|
|
659
|
+
console.log(chalk.gray(`\n Cloned repository retained at: ${gitClone.tempDir}`));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
627
663
|
}
|
|
628
664
|
|
|
629
665
|
/**
|
package/cli/commands/scan.js
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
import { isHighEntropyMatch, getConfidence } from '../utils/entropy.js';
|
|
39
39
|
import * as output from '../utils/output.js';
|
|
40
40
|
import { CacheManager } from '../utils/cache-manager.js';
|
|
41
|
+
import { isGitUrl, cloneGitRepo } from '../core/git-clone.js';
|
|
41
42
|
|
|
42
43
|
// =============================================================================
|
|
43
44
|
// CUSTOM PATTERNS (.praxis.json)
|
|
@@ -86,10 +87,37 @@ function loadCustomPatterns(rootPath) {
|
|
|
86
87
|
// =============================================================================
|
|
87
88
|
|
|
88
89
|
export async function scanCommand(targetPath = '.', options = {}) {
|
|
89
|
-
|
|
90
|
+
let gitClone = null;
|
|
91
|
+
let effectivePath = targetPath;
|
|
92
|
+
|
|
93
|
+
if (isGitUrl(targetPath)) {
|
|
94
|
+
const gitSpinner = ora({ text: chalk.cyan(`Cloning remote Git repository: ${targetPath}...`), color: 'cyan' }).start();
|
|
95
|
+
try {
|
|
96
|
+
gitClone = cloneGitRepo(targetPath, {
|
|
97
|
+
branch: options.branch,
|
|
98
|
+
depth: options.depth,
|
|
99
|
+
gitToken: options.gitToken,
|
|
100
|
+
submodules: options.submodules,
|
|
101
|
+
});
|
|
102
|
+
effectivePath = gitClone.tempDir;
|
|
103
|
+
gitSpinner.succeed(chalk.green(`Cloned ${gitClone.repoName} (${gitClone.displayUrl})`));
|
|
104
|
+
} catch (err) {
|
|
105
|
+
gitSpinner.fail(chalk.red(err.message));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const cleanup = () => {
|
|
111
|
+
if (gitClone && !options.keepClone) {
|
|
112
|
+
gitClone.cleanup();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const absolutePath = path.resolve(effectivePath);
|
|
90
117
|
|
|
91
118
|
// Validate path exists
|
|
92
119
|
if (!fs.existsSync(absolutePath)) {
|
|
120
|
+
cleanup();
|
|
93
121
|
output.error(`Path does not exist: ${absolutePath}`);
|
|
94
122
|
process.exit(1);
|
|
95
123
|
}
|
|
@@ -209,10 +237,12 @@ export async function scanCommand(targetPath = '.', options = {}) {
|
|
|
209
237
|
}
|
|
210
238
|
|
|
211
239
|
// Exit with appropriate code
|
|
240
|
+
cleanup();
|
|
212
241
|
const hasFindings = allResults.length > 0;
|
|
213
242
|
process.exit(hasFindings ? 1 : 0);
|
|
214
243
|
|
|
215
244
|
} catch (err) {
|
|
245
|
+
cleanup();
|
|
216
246
|
spinner.fail('Scan failed');
|
|
217
247
|
output.error(err.message);
|
|
218
248
|
process.exit(1);
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"generatedAt": "2026-10-02T18:49:22.592Z",
|
|
4
|
+
"rootPath": "C:\\STUDY\\Github\\CADRE-Platform\\Praxis\\cli\\core",
|
|
5
|
+
"recon": {
|
|
6
|
+
"frameworks": [],
|
|
7
|
+
"languages": [
|
|
8
|
+
"javascript"
|
|
9
|
+
],
|
|
10
|
+
"apiRoutes": [],
|
|
11
|
+
"authPatterns": [],
|
|
12
|
+
"databases": [],
|
|
13
|
+
"cloudProviders": [],
|
|
14
|
+
"frontendExposure": [],
|
|
15
|
+
"packageManagers": [],
|
|
16
|
+
"cicd": [],
|
|
17
|
+
"hasDockerfile": false,
|
|
18
|
+
"hasTerraform": false,
|
|
19
|
+
"hasKubernetes": false,
|
|
20
|
+
"envFiles": [],
|
|
21
|
+
"configFiles": [],
|
|
22
|
+
"hasModelFiles": false,
|
|
23
|
+
"modelFiles": []
|
|
24
|
+
},
|
|
25
|
+
"fileIndex": {
|
|
26
|
+
"branding.js": {
|
|
27
|
+
"hash": "fa5bc771350efb48054cb614633a03a666b9ad5dd8d871dab41778076a71ab29",
|
|
28
|
+
"size": 1842,
|
|
29
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
30
|
+
},
|
|
31
|
+
"env.js": {
|
|
32
|
+
"hash": "2329f75078712d12885da3fdeed8323080102994dc1a80060c943b7e4c449f61",
|
|
33
|
+
"size": 1118,
|
|
34
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
35
|
+
},
|
|
36
|
+
"errors.js": {
|
|
37
|
+
"hash": "bfb1c9b324bca53a2bc52aa89e0111d0a4b66bb7758349a78f0cf9370d66197d",
|
|
38
|
+
"size": 1785,
|
|
39
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
40
|
+
},
|
|
41
|
+
"fs.js": {
|
|
42
|
+
"hash": "891d0ea6d876b57b9b6c87b32eaec3fa288cb02843198f00591fd5856aa01e62",
|
|
43
|
+
"size": 1786,
|
|
44
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
45
|
+
},
|
|
46
|
+
"version.js": {
|
|
47
|
+
"hash": "9aebd648284fa4cec1b956af4ac49be716b5aa6cf328854160a42e349c0315ac",
|
|
48
|
+
"size": 2489,
|
|
49
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
50
|
+
},
|
|
51
|
+
"ast/guardrail-detector.js": {
|
|
52
|
+
"hash": "d8d43bf9158eca11e3299b54547b9e77677f791e8b388878bc270e039bca9c2b",
|
|
53
|
+
"size": 5241,
|
|
54
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
55
|
+
},
|
|
56
|
+
"ast/index.js": {
|
|
57
|
+
"hash": "8ce6688a476826aa1a87071b6e0b6bce033e30573e4732a8fc48d5b800596012",
|
|
58
|
+
"size": 960,
|
|
59
|
+
"lastScanned": "2026-10-02T18:49:22.590Z"
|
|
60
|
+
},
|
|
61
|
+
"ast/parser.js": {
|
|
62
|
+
"hash": "c277d9fbf8029afdf4018a8d4794596c7ef29260e765e67fb4eb8f6930ffa99e",
|
|
63
|
+
"size": 21531,
|
|
64
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
65
|
+
},
|
|
66
|
+
"ast/scope-tree.js": {
|
|
67
|
+
"hash": "ab409fe692cddd921bb9733a78357b598db00da1e5ada8cb69497b416f641d77",
|
|
68
|
+
"size": 7126,
|
|
69
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
70
|
+
},
|
|
71
|
+
"ast/taint-tracker.js": {
|
|
72
|
+
"hash": "c225e3f85c57f4f2831300b42426d5119e612b23839f3a25e2035bf9d5417476",
|
|
73
|
+
"size": 6316,
|
|
74
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
75
|
+
},
|
|
76
|
+
"web/jobs.js": {
|
|
77
|
+
"hash": "91bef088c5f2d713300063ee65b0f6065e51a29432a96a7ac7e101cec05fcd19",
|
|
78
|
+
"size": 7025,
|
|
79
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
80
|
+
},
|
|
81
|
+
"web/projects.js": {
|
|
82
|
+
"hash": "6961f8ee3ea4a1d18620cd4d2ab802ddc5db17db106743990ac91af870b006c5",
|
|
83
|
+
"size": 5118,
|
|
84
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
85
|
+
},
|
|
86
|
+
"web/server.js": {
|
|
87
|
+
"hash": "fba1f8c517d3042520788336fb4efa6741dc7221fda2b04e0d58d7085323582f",
|
|
88
|
+
"size": 14108,
|
|
89
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
90
|
+
},
|
|
91
|
+
"web/ui.js": {
|
|
92
|
+
"hash": "c582824d031a66d3f068c594540b177c129d90259c366d522db65cd5dd41134c",
|
|
93
|
+
"size": 75416,
|
|
94
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
95
|
+
},
|
|
96
|
+
"output/compliance.js": {
|
|
97
|
+
"hash": "830b66d76e9029d0d569d312b5740d9d521b2408858dce519bb0048a25205ab0",
|
|
98
|
+
"size": 4057,
|
|
99
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
100
|
+
},
|
|
101
|
+
"output/html-theme.js": {
|
|
102
|
+
"hash": "33cacad539fb303e1e33f4cdd6868599d3824d4ea6f1d4750bdefbba21461fcd",
|
|
103
|
+
"size": 6823,
|
|
104
|
+
"lastScanned": "2026-10-02T18:49:22.591Z"
|
|
105
|
+
},
|
|
106
|
+
"output/index.js": {
|
|
107
|
+
"hash": "dfff64688caed4822b117bbb75ddd796666e53b2ff15b3a868abb94954d2db9a",
|
|
108
|
+
"size": 1503,
|
|
109
|
+
"lastScanned": "2026-10-02T18:49:22.592Z"
|
|
110
|
+
},
|
|
111
|
+
"output/json.js": {
|
|
112
|
+
"hash": "29cdf0b268b658fb24badacca9fe1a42e996acc5c25f6601ebde529a1501c8c7",
|
|
113
|
+
"size": 1427,
|
|
114
|
+
"lastScanned": "2026-10-02T18:49:22.592Z"
|
|
115
|
+
},
|
|
116
|
+
"output/sarif.js": {
|
|
117
|
+
"hash": "0b209b9d403e48859511880ea7caf36f8f435dafe80dc7318bc9f46bb748cc2c",
|
|
118
|
+
"size": 8863,
|
|
119
|
+
"lastScanned": "2026-10-02T18:49:22.592Z"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"lastFindings": {
|
|
123
|
+
"cli/__tests__/ast-engine.test.js": [
|
|
124
|
+
{
|
|
125
|
+
"line": 0,
|
|
126
|
+
"column": 0,
|
|
127
|
+
"severity": "high",
|
|
128
|
+
"category": "history",
|
|
129
|
+
"rule": "GIT_HISTORY_SECRET",
|
|
130
|
+
"title": "Historical Secret: Generic API Key Assignment",
|
|
131
|
+
"description": "Secret was removed from code but still exists in git history (commit 322700ad27, Tue Aug 18 11:19:47 2026 +0530). Anyone with repo access can retrieve it.",
|
|
132
|
+
"matched": "API_KEY ***123\"",
|
|
133
|
+
"confidence": "low",
|
|
134
|
+
"cwe": null,
|
|
135
|
+
"owasp": null,
|
|
136
|
+
"fix": "Rotate this credential immediately, then clean history: npx bfg --replace-text passwords.txt"
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
"ast/guardrail-detector.js": [
|
|
140
|
+
{
|
|
141
|
+
"line": 70,
|
|
142
|
+
"column": 0,
|
|
143
|
+
"severity": "high",
|
|
144
|
+
"category": "llm",
|
|
145
|
+
"rule": "AI_EVAL_HARNESS_DISABLED_GUARDRAILS",
|
|
146
|
+
"title": "Eval Harness Disables Safety Guardrails",
|
|
147
|
+
"description": "An evaluation harness or agent-sandbox config disables model safety guardrails. This is the configuration class behind the OpenAI–Hugging Face July 2026 incident (models evaluated with cyber refusals disabled), and enables unchecked autonomous behavior during evaluation.",
|
|
148
|
+
"matched": "AI_EVAL_HARNESS_DISABLED_GUARDRAILS",
|
|
149
|
+
"confidence": "medium",
|
|
150
|
+
"cwe": "CWE-284",
|
|
151
|
+
"owasp": "ASI08",
|
|
152
|
+
"fix": "Keep safety guardrails enabled during evaluation, or gate their removal behind explicit containment controls (no internet, isolated egress)."
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
"web/server.js": [
|
|
156
|
+
{
|
|
157
|
+
"line": 171,
|
|
158
|
+
"column": 46,
|
|
159
|
+
"severity": "medium",
|
|
160
|
+
"category": "ssrf",
|
|
161
|
+
"rule": "SSRF_INTERNAL_IP",
|
|
162
|
+
"title": "SSRF: Internal IP Pattern",
|
|
163
|
+
"description": "Internal IP address in code. Verify it is not reachable via user-controlled URLs.",
|
|
164
|
+
"matched": "127.0.0.1",
|
|
165
|
+
"confidence": "low",
|
|
166
|
+
"cwe": "CWE-918",
|
|
167
|
+
"owasp": "A10:2021",
|
|
168
|
+
"fix": "Block private IP ranges in URL validation for user-supplied URLs"
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"../../../../../../Users/Ganro/.cursor/mcp.json": [
|
|
172
|
+
{
|
|
173
|
+
"line": 1,
|
|
174
|
+
"column": 0,
|
|
175
|
+
"severity": "medium",
|
|
176
|
+
"category": "llm",
|
|
177
|
+
"rule": "MCP_SHADOW_CONFIG",
|
|
178
|
+
"title": "MCP: 1 Shadow Server(s) in User Config",
|
|
179
|
+
"description": "Found 1 MCP server(s) configured outside the project in C:\\Users\\Ganro\\.cursor\\mcp.json. These operate outside your project's security controls.",
|
|
180
|
+
"matched": "redstrike",
|
|
181
|
+
"confidence": "medium",
|
|
182
|
+
"cwe": "CWE-269",
|
|
183
|
+
"owasp": "A05:2021",
|
|
184
|
+
"fix": "Review shadow MCP servers. Move project-specific servers to the project mcp.json and track in version control."
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
"observability": [
|
|
188
|
+
{
|
|
189
|
+
"line": 1,
|
|
190
|
+
"column": 0,
|
|
191
|
+
"severity": "medium",
|
|
192
|
+
"category": "llm",
|
|
193
|
+
"rule": "NO_OBSERVABILITY",
|
|
194
|
+
"title": "AI in Use Without Observability/Tracing",
|
|
195
|
+
"description": "This project calls LLMs, but no AI tracing/observability wiring (LangSmith, Langfuse, Helicone, OpenTelemetry, ...) was found outside dependency manifests. Without traces you cannot detect prompt-injection events, monitor output quality, or respond to AI incidents — EU AI Act Art. 12 / ISO 42001 A.6.2.6.",
|
|
196
|
+
"matched": "no tracing wiring in 19 evidence file(s)",
|
|
197
|
+
"confidence": "medium",
|
|
198
|
+
"cwe": "CWE-778",
|
|
199
|
+
"owasp": "ASI03",
|
|
200
|
+
"fix": "Wire an AI tracing/observability stack (e.g., Langfuse, Helicone, or OpenTelemetry) and log prompts, tool calls, and outputs with retention."
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
"stats": {
|
|
205
|
+
"totalFiles": 19,
|
|
206
|
+
"totalFindings": 5,
|
|
207
|
+
"lastScore": 86.2,
|
|
208
|
+
"lastGrade": "B"
|
|
209
|
+
}
|
|
210
|
+
}
|