praxis-sec 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/praxis-architecture.svg +304 -0
- package/assets/praxis-logo.svg +38 -0
- package/cli/agents/abom-generator.js +1 -1
- package/cli/agents/agent-attestation-agent.js +10 -1
- package/cli/agents/agent-config-scanner.js +1 -1
- package/cli/agents/ai-infra-inventory-agent.js +482 -482
- package/cli/agents/base-agent.js +1 -1
- package/cli/agents/endpoint-agent-abuse-agent.js +1 -1
- package/cli/agents/html-reporter.js +10 -10
- package/cli/agents/index.js +2 -2
- package/cli/agents/injection-tester.js +8 -1
- package/cli/agents/mcp-security-agent.js +600 -594
- package/cli/agents/memory-poisoning-agent.js +1 -1
- package/cli/agents/model-file-scanner.js +1 -1
- package/cli/agents/orchestrator.js +375 -355
- package/cli/agents/prompt-injection-prober.js +228 -228
- package/cli/bin/praxis.js +7 -3
- package/cli/commands/agent-fix.js +1091 -1245
- package/cli/commands/audit.js +1232 -1216
- package/cli/commands/baseline.js +3 -2
- package/cli/commands/benchmark.js +1 -1
- package/cli/commands/ci.js +50 -25
- package/cli/commands/deps.js +11 -5
- package/cli/commands/diff.js +2 -1
- package/cli/commands/env-audit.js +1 -1
- package/cli/commands/fix.js +1 -1
- package/cli/commands/legal.js +2 -1
- package/cli/commands/mcp.js +3 -2
- package/cli/commands/openclaw.js +3 -6
- package/cli/commands/red-team.js +351 -350
- package/cli/commands/remediate.js +1 -1
- package/cli/commands/rotate.js +1 -1
- package/cli/commands/rules.js +1 -1
- package/cli/commands/scan-standard.js +3 -6
- package/cli/commands/scan.js +554 -554
- package/cli/commands/score.js +1 -1
- package/cli/commands/undo.js +22 -77
- package/cli/commands/vibe-check.js +3 -2
- package/cli/commands/watch.js +6 -5
- package/cli/core/fix-plan.js +274 -0
- package/cli/core/fs.js +27 -0
- package/cli/core/git-clone.js +8 -6
- package/cli/core/glob.js +56 -0
- package/cli/core/output/html-theme.js +158 -158
- package/cli/core/output/json.js +56 -48
- package/cli/core/output/sarif.js +2 -2
- package/cli/core/paths.js +91 -0
- package/cli/core/web/jobs.js +2 -2
- package/cli/core/web/server.js +19 -8
- package/cli/data/threatpacks/latest.json +41 -41
- package/cli/integrations/github-action.js +136 -0
- package/cli/utils/cache-manager.js +2 -1
- package/cli/utils/plugin-loader.js +15 -95
- package/cli/utils/rule-import.js +227 -227
- package/cli/utils/rule-registry.js +425 -425
- package/cli/utils/scan-fingerprint.js +1 -1
- package/cli/utils/score-history.js +118 -118
- package/docs/USAGE.md +16 -9
- package/docs/design/WEB-UI.md +4 -5
- package/package.json +81 -71
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Scan fingerprint — identify exactly what produced a set of findings.
|
|
3
3
|
* ============================================================================
|
|
4
4
|
*
|
|
5
|
-
* Why this exists
|
|
5
|
+
* Why this exists: during self-dogfooding a scan reported 325 findings
|
|
6
6
|
* where four subsequent scans of the identical tree reported 308. The extra 17 were
|
|
7
7
|
* `PROBE_*` / `VIBE_*` rules in files nobody had touched. Data drift, test fixtures
|
|
8
8
|
* and the scan cache were each ruled out, so the cause stayed unknown.
|
|
@@ -1,119 +1,119 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Score history — read the scan-over-time series behind the trend section.
|
|
3
|
-
* ============================================================================
|
|
4
|
-
*
|
|
5
|
-
* `.praxis/history.json` is appended once per scan with `{timestamp, score, grade,
|
|
6
|
-
* totalFindings, totalDepVulns, categoryScores}`.
|
|
7
|
-
*
|
|
8
|
-
* The discipline here is about *not overstating* the data
|
|
9
|
-
* - with no prior scans the project is at its **baseline** — the report must say so
|
|
10
|
-
* rather than draw an empty graph, which reads as "flat, no change"
|
|
11
|
-
* - a two-point series is **two measurements, not a trend**, and is labelled as such
|
|
12
|
-
* - the sample size and the time span are always shown next to the chart, so nobody
|
|
13
|
-
* reads three scans over four minutes as a security trajectory
|
|
14
|
-
*
|
|
15
|
-
* Best-effort and never throws: a corrupt history must not break a report, and it must
|
|
16
|
-
* never be silently replaced with fabricated points.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import fs from 'fs';
|
|
20
|
-
import path from 'path';
|
|
21
|
-
|
|
22
|
-
const HISTORY_FILE = path.join('.praxis', 'history.json');
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Reads the score history for a project.
|
|
26
|
-
* @returns {{points: object[], unreadable: number, error: string|null}}
|
|
27
|
-
*/
|
|
28
|
-
export function readScoreHistory(rootPath = process.cwd()) {
|
|
29
|
-
const root = typeof rootPath === 'string' && rootPath.length > 0 ? rootPath : process.cwd();
|
|
30
|
-
let raw;
|
|
31
|
-
try {
|
|
32
|
-
raw = fs.readFileSync(path.join(root, HISTORY_FILE), 'utf8');
|
|
33
|
-
} catch (err) {
|
|
34
|
-
return { points: [], unreadable: 0, error: err.code === 'ENOENT' ? null : err.message };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let parsed;
|
|
38
|
-
try {
|
|
39
|
-
parsed = JSON.parse(raw);
|
|
40
|
-
} catch (err) {
|
|
41
|
-
return { points: [], unreadable: 0, error: `history.json is not valid JSON: ${err.message}` };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!Array.isArray(parsed)) {
|
|
45
|
-
return { points: [], unreadable: 0, error: 'history.json is not an array of scan entries' };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const points = [];
|
|
49
|
-
let unreadable = 0;
|
|
50
|
-
for (const entry of parsed) {
|
|
51
|
-
if (entry && typeof entry === 'object' && typeof entry.score === 'number') {
|
|
52
|
-
points.push({
|
|
53
|
-
timestamp: entry.timestamp || null,
|
|
54
|
-
score: entry.score,
|
|
55
|
-
grade: entry.grade ?? null,
|
|
56
|
-
totalFindings: typeof entry.totalFindings === 'number' ? entry.totalFindings : null,
|
|
57
|
-
});
|
|
58
|
-
} else {
|
|
59
|
-
unreadable++;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Oldest → newest, so callers can treat the tail as "latest".
|
|
64
|
-
points.sort((a, b) => String(a.timestamp ?? '').localeCompare(String(b.timestamp ?? '')));
|
|
65
|
-
return { points, unreadable, error: null };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Summarises a series for display, and states plainly how much data there is.
|
|
70
|
-
*
|
|
71
|
-
* @param {object[]} points History points, oldest first (from readScoreHistory).
|
|
72
|
-
* @param {object} [current] `{score, grade}` for the scan being reported, so the
|
|
73
|
-
* live score appears on the chart even before it is
|
|
74
|
-
* appended to history.
|
|
75
|
-
*/
|
|
76
|
-
export function summarizeHistory(points = [], current = null) {
|
|
77
|
-
// Coerce first: spreading null/undefined throws, which would break the
|
|
78
|
-
// "never throws" contract this module advertises.
|
|
79
|
-
const series = Array.isArray(points) ? [...points] : [];
|
|
80
|
-
|
|
81
|
-
// Append the in-flight scan if history doesn't already end with it. Without this the
|
|
82
|
-
// chart would show the *previous* score as the latest point and quietly mislead.
|
|
83
|
-
const last = series[series.length - 1];
|
|
84
|
-
const currentAlreadyPresent =
|
|
85
|
-
current && typeof current.score === 'number' && last && last.score === current.score;
|
|
86
|
-
if (current && typeof current.score === 'number' && !currentAlreadyPresent) {
|
|
87
|
-
series.push({ timestamp: null, score: current.score, grade: current.grade ?? null, totalFindings: null, isCurrent: true });
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const scores = series.map(p => p.score);
|
|
91
|
-
const latest = scores[scores.length - 1] ?? null;
|
|
92
|
-
const previous = scores.length >= 2 ? scores[scores.length - 2] : null;
|
|
93
|
-
|
|
94
|
-
// How many *measurements* exist, and how many are prior runs (excluding the live one).
|
|
95
|
-
const measurementCount = scores.length;
|
|
96
|
-
const priorCount = Math.max(0, measurementCount - (current && !currentAlreadyPresent ? 1 : 0));
|
|
97
|
-
|
|
98
|
-
let spanDays = null;
|
|
99
|
-
const dated = series.filter(p => p.timestamp);
|
|
100
|
-
if (dated.length >= 2) {
|
|
101
|
-
const t0 = Date.parse(dated[0].timestamp);
|
|
102
|
-
const tN = Date.parse(dated[dated.length - 1].timestamp);
|
|
103
|
-
if (Number.isFinite(t0) && Number.isFinite(tN)) spanDays = Math.round(((tN - t0) / 86400000) * 10) / 10;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
series,
|
|
108
|
-
measurementCount,
|
|
109
|
-
priorCount,
|
|
110
|
-
latest,
|
|
111
|
-
previous,
|
|
112
|
-
delta: latest !== null && previous !== null ? Math.round((latest - previous) * 10) / 10 : null,
|
|
113
|
-
best: scores.length ? Math.max(...scores) : null,
|
|
114
|
-
worst: scores.length ? Math.min(...scores) : null,
|
|
115
|
-
isBaseline: measurementCount <= 1 && !current,
|
|
116
|
-
needsMoreData: measurementCount >= 1 && measurementCount < 3,
|
|
117
|
-
spanDays,
|
|
118
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Score history — read the scan-over-time series behind the trend section.
|
|
3
|
+
* ============================================================================
|
|
4
|
+
*
|
|
5
|
+
* `.praxis/history.json` is appended once per scan with `{timestamp, score, grade,
|
|
6
|
+
* totalFindings, totalDepVulns, categoryScores}`.
|
|
7
|
+
*
|
|
8
|
+
* The discipline here is about *not overstating* the data:
|
|
9
|
+
* - with no prior scans the project is at its **baseline** — the report must say so
|
|
10
|
+
* rather than draw an empty graph, which reads as "flat, no change"
|
|
11
|
+
* - a two-point series is **two measurements, not a trend**, and is labelled as such
|
|
12
|
+
* - the sample size and the time span are always shown next to the chart, so nobody
|
|
13
|
+
* reads three scans over four minutes as a security trajectory
|
|
14
|
+
*
|
|
15
|
+
* Best-effort and never throws: a corrupt history must not break a report, and it must
|
|
16
|
+
* never be silently replaced with fabricated points.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import fs from 'fs';
|
|
20
|
+
import path from 'path';
|
|
21
|
+
|
|
22
|
+
const HISTORY_FILE = path.join('.praxis', 'history.json');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Reads the score history for a project.
|
|
26
|
+
* @returns {{points: object[], unreadable: number, error: string|null}}
|
|
27
|
+
*/
|
|
28
|
+
export function readScoreHistory(rootPath = process.cwd()) {
|
|
29
|
+
const root = typeof rootPath === 'string' && rootPath.length > 0 ? rootPath : process.cwd();
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = fs.readFileSync(path.join(root, HISTORY_FILE), 'utf8');
|
|
33
|
+
} catch (err) {
|
|
34
|
+
return { points: [], unreadable: 0, error: err.code === 'ENOENT' ? null : err.message };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
return { points: [], unreadable: 0, error: `history.json is not valid JSON: ${err.message}` };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!Array.isArray(parsed)) {
|
|
45
|
+
return { points: [], unreadable: 0, error: 'history.json is not an array of scan entries' };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const points = [];
|
|
49
|
+
let unreadable = 0;
|
|
50
|
+
for (const entry of parsed) {
|
|
51
|
+
if (entry && typeof entry === 'object' && typeof entry.score === 'number') {
|
|
52
|
+
points.push({
|
|
53
|
+
timestamp: entry.timestamp || null,
|
|
54
|
+
score: entry.score,
|
|
55
|
+
grade: entry.grade ?? null,
|
|
56
|
+
totalFindings: typeof entry.totalFindings === 'number' ? entry.totalFindings : null,
|
|
57
|
+
});
|
|
58
|
+
} else {
|
|
59
|
+
unreadable++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Oldest → newest, so callers can treat the tail as "latest".
|
|
64
|
+
points.sort((a, b) => String(a.timestamp ?? '').localeCompare(String(b.timestamp ?? '')));
|
|
65
|
+
return { points, unreadable, error: null };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Summarises a series for display, and states plainly how much data there is.
|
|
70
|
+
*
|
|
71
|
+
* @param {object[]} points History points, oldest first (from readScoreHistory).
|
|
72
|
+
* @param {object} [current] `{score, grade}` for the scan being reported, so the
|
|
73
|
+
* live score appears on the chart even before it is
|
|
74
|
+
* appended to history.
|
|
75
|
+
*/
|
|
76
|
+
export function summarizeHistory(points = [], current = null) {
|
|
77
|
+
// Coerce first: spreading null/undefined throws, which would break the
|
|
78
|
+
// "never throws" contract this module advertises.
|
|
79
|
+
const series = Array.isArray(points) ? [...points] : [];
|
|
80
|
+
|
|
81
|
+
// Append the in-flight scan if history doesn't already end with it. Without this the
|
|
82
|
+
// chart would show the *previous* score as the latest point and quietly mislead.
|
|
83
|
+
const last = series[series.length - 1];
|
|
84
|
+
const currentAlreadyPresent =
|
|
85
|
+
current && typeof current.score === 'number' && last && last.score === current.score;
|
|
86
|
+
if (current && typeof current.score === 'number' && !currentAlreadyPresent) {
|
|
87
|
+
series.push({ timestamp: null, score: current.score, grade: current.grade ?? null, totalFindings: null, isCurrent: true });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const scores = series.map(p => p.score);
|
|
91
|
+
const latest = scores[scores.length - 1] ?? null;
|
|
92
|
+
const previous = scores.length >= 2 ? scores[scores.length - 2] : null;
|
|
93
|
+
|
|
94
|
+
// How many *measurements* exist, and how many are prior runs (excluding the live one).
|
|
95
|
+
const measurementCount = scores.length;
|
|
96
|
+
const priorCount = Math.max(0, measurementCount - (current && !currentAlreadyPresent ? 1 : 0));
|
|
97
|
+
|
|
98
|
+
let spanDays = null;
|
|
99
|
+
const dated = series.filter(p => p.timestamp);
|
|
100
|
+
if (dated.length >= 2) {
|
|
101
|
+
const t0 = Date.parse(dated[0].timestamp);
|
|
102
|
+
const tN = Date.parse(dated[dated.length - 1].timestamp);
|
|
103
|
+
if (Number.isFinite(t0) && Number.isFinite(tN)) spanDays = Math.round(((tN - t0) / 86400000) * 10) / 10;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
series,
|
|
108
|
+
measurementCount,
|
|
109
|
+
priorCount,
|
|
110
|
+
latest,
|
|
111
|
+
previous,
|
|
112
|
+
delta: latest !== null && previous !== null ? Math.round((latest - previous) * 10) / 10 : null,
|
|
113
|
+
best: scores.length ? Math.max(...scores) : null,
|
|
114
|
+
worst: scores.length ? Math.min(...scores) : null,
|
|
115
|
+
isBaseline: measurementCount <= 1 && !current,
|
|
116
|
+
needsMoreData: measurementCount >= 1 && measurementCount < 3,
|
|
117
|
+
spanDays,
|
|
118
|
+
};
|
|
119
119
|
}
|
package/docs/USAGE.md
CHANGED
|
@@ -896,8 +896,8 @@ praxis project policy init
|
|
|
896
896
|
|
|
897
897
|
### `.praxis/agents/*.js`
|
|
898
898
|
|
|
899
|
-
Custom agent plugins,
|
|
900
|
-
|
|
899
|
+
Custom agent plugins, loaded only when a local scan uses `--trust-plugins`.
|
|
900
|
+
Scaffold one with:
|
|
901
901
|
|
|
902
902
|
```bash
|
|
903
903
|
praxis project plugins new my-rule
|
|
@@ -906,7 +906,7 @@ praxis project plugins new my-rule
|
|
|
906
906
|
A plugin is any module exporting a class extending `BaseAgent`:
|
|
907
907
|
|
|
908
908
|
```js
|
|
909
|
-
import { BaseAgent, createFinding } from 'praxis';
|
|
909
|
+
import { BaseAgent, createFinding } from 'praxis-sec';
|
|
910
910
|
|
|
911
911
|
export default class MyAgent extends BaseAgent {
|
|
912
912
|
constructor() { super('MyAgent', 'description', 'category'); }
|
|
@@ -961,7 +961,7 @@ only ever emits a known class name.
|
|
|
961
961
|
prints a provenance line in its footer:
|
|
962
962
|
|
|
963
963
|
```
|
|
964
|
-
praxis 1.
|
|
964
|
+
praxis 1.2.2 · node v24.14.1 · probes v1.1(23) · threatpack v1.1(3) · eaa v0.1.0 · files 215
|
|
965
965
|
```
|
|
966
966
|
|
|
967
967
|
It records the tool version, the runtime, and the version of every vendored data asset
|
|
@@ -1095,14 +1095,21 @@ praxis fix undo --all
|
|
|
1095
1095
|
|
|
1096
1096
|
## Custom plugins
|
|
1097
1097
|
|
|
1098
|
-
Praxis
|
|
1099
|
-
|
|
1100
|
-
|
|
1098
|
+
Praxis loads plugins from `.praxis/agents/` only with explicit trust:
|
|
1099
|
+
|
|
1100
|
+
```bash
|
|
1101
|
+
praxis scan full . --trust-plugins
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
Plugins execute arbitrary JavaScript with your permissions. Remote repository and
|
|
1105
|
+
web scans do not execute target plugins. Programmatic callers opt in with
|
|
1106
|
+
`buildOrchestratorAsync(rootPath, { trustPlugins: true })`; the synchronous
|
|
1107
|
+
`buildOrchestrator()` uses only built-in agents.
|
|
1101
1108
|
|
|
1102
1109
|
A plugin extends `BaseAgent` and follows the standard contract:
|
|
1103
1110
|
|
|
1104
1111
|
```js
|
|
1105
|
-
import { BaseAgent, createFinding } from 'praxis';
|
|
1112
|
+
import { BaseAgent, createFinding } from 'praxis-sec';
|
|
1106
1113
|
|
|
1107
1114
|
export default class HardcodedAdminCheck extends BaseAgent {
|
|
1108
1115
|
constructor() {
|
|
@@ -1151,7 +1158,7 @@ plugin-side wiring required.
|
|
|
1151
1158
|
| --- | --- |
|
|
1152
1159
|
| `praxis intel update` is slow | NVD rate-limits to 6s/request without a key. Set `NVD_API_KEY`. |
|
|
1153
1160
|
| `praxis scan ci --strict-intel` fails locally | Run `praxis intel update` first. Default freshness window is `7d`. |
|
|
1154
|
-
| Custom plugins not loading |
|
|
1161
|
+
| Custom plugins not loading | Use `praxis scan full . --trust-plugins` for a trusted local project, or `buildOrchestratorAsync(rootPath, { trustPlugins: true })`. Remote and web scans use built-in agents only. |
|
|
1155
1162
|
| `node cli/bin/praxis.js` works but `praxis` doesn't | Run `npm link` once (or `npm install -g .` from the repo). |
|
|
1156
1163
|
| Test files report secrets | Confidence is auto-downgraded in test/doc/example paths — but use `praxis-ignore` for explicit suppression. |
|
|
1157
1164
|
| Feed lives somewhere else | Override `HOME` (Unix) or `USERPROFILE` (Windows) — used by the test suite. |
|
package/docs/design/WEB-UI.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Praxis Web UI — design & threat model
|
|
2
2
|
|
|
3
|
-
> **Status:**
|
|
4
|
-
> Feature tracking: `P-IMP-052` in `docs/internal/IMPROVEMENT-PLAN.md`.
|
|
3
|
+
> **Status:** implemented — read-only scan orchestration, served locally by the CLI.
|
|
5
4
|
>
|
|
6
5
|
> This document exists because a web UI for Praxis is the one place where adding a
|
|
7
6
|
> feature means adding an **attack surface** to a security tool. A CLI runs with the
|
|
@@ -21,9 +20,9 @@ A local web app for driving scans and managing scan projects:
|
|
|
21
20
|
- manage baselines
|
|
22
21
|
|
|
23
22
|
Reuses the existing `Orchestrator` as the scan engine — the UI never reimplements
|
|
24
|
-
detection. All UI is **vanilla JS served from the CLI**,
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
detection. All UI is **vanilla JS served from the CLI**, with no bundler, no
|
|
24
|
+
framework, and no network fetch of assets. Reports use the shared
|
|
25
|
+
`cli/core/output/html-theme.js`.
|
|
27
26
|
|
|
28
27
|
## 2. Threat model — the part that matters
|
|
29
28
|
|
package/package.json
CHANGED
|
@@ -1,71 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "praxis-sec",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "Praxis — From finding to fix, on autopilot. AI-native security CLI with 28 parallel agents covering AI / MCP / skill threats, OWASP LLM Top 10, multi-source supply-chain intel (OSV / GHSA / KEV / EPSS / NVD / Gitleaks + optional paid feeds), secret scanning, and an LLM-powered agentic remediation loop.",
|
|
5
|
-
"main": "cli/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"praxis": "cli/bin/praxis.js"
|
|
8
|
-
},
|
|
9
|
-
"type": "module",
|
|
10
|
-
"scripts": {
|
|
11
|
-
"test": "node --test cli/__tests__/intel.test.js cli/__tests__/agents.test.js cli/__tests__/core.test.js cli/__tests__/standards.test.js cli/__tests__/model-file-scanner.test.js cli/__tests__/prompt-injection-prober.test.js cli/__tests__/ast-engine.test.js cli/__tests__/benchmark.test.js cli/__tests__/redteam.test.js cli/__tests__/html-reporter.test.js cli/__tests__/scan-fingerprint.test.js cli/__tests__/fix-ledger.test.js cli/__tests__/threatpack-probes.test.js cli/__tests__/score-history.test.js cli/__tests__/web-ui.test.js cli/__tests__/rule-portability.test.js cli/__tests__/git-remote.test.js",
|
|
12
|
-
"test:determinism": "node scripts/check-determinism.mjs .",
|
|
13
|
-
"lint": "eslint cli/",
|
|
14
|
-
"praxis": "node cli/bin/praxis.js"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "praxis-sec",
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "Praxis — From finding to fix, on autopilot. AI-native security CLI with 28 parallel agents covering AI / MCP / skill threats, OWASP LLM Top 10, multi-source supply-chain intel (OSV / GHSA / KEV / EPSS / NVD / Gitleaks + optional paid feeds), secret scanning, and an LLM-powered agentic remediation loop.",
|
|
5
|
+
"main": "cli/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"praxis": "cli/bin/praxis.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test cli/__tests__/intel.test.js cli/__tests__/agents.test.js cli/__tests__/core.test.js cli/__tests__/standards.test.js cli/__tests__/model-file-scanner.test.js cli/__tests__/prompt-injection-prober.test.js cli/__tests__/ast-engine.test.js cli/__tests__/benchmark.test.js cli/__tests__/redteam.test.js cli/__tests__/html-reporter.test.js cli/__tests__/scan-fingerprint.test.js cli/__tests__/fix-ledger.test.js cli/__tests__/threatpack-probes.test.js cli/__tests__/score-history.test.js cli/__tests__/web-ui.test.js cli/__tests__/rule-portability.test.js cli/__tests__/git-remote.test.js cli/__tests__/release-reliability.test.js",
|
|
12
|
+
"test:determinism": "node scripts/check-determinism.mjs .",
|
|
13
|
+
"lint": "eslint cli/",
|
|
14
|
+
"praxis": "node cli/bin/praxis.js",
|
|
15
|
+
"prepublishOnly": "npm test && npm run lint && npm run test:determinism"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"security",
|
|
19
|
+
"praxis",
|
|
20
|
+
"ai-security",
|
|
21
|
+
"agentic-security",
|
|
22
|
+
"llm-security",
|
|
23
|
+
"mcp-security",
|
|
24
|
+
"prompt-injection",
|
|
25
|
+
"supply-chain",
|
|
26
|
+
"secrets",
|
|
27
|
+
"scanner",
|
|
28
|
+
"sast",
|
|
29
|
+
"devsecops",
|
|
30
|
+
"red-team",
|
|
31
|
+
"vulnerability-scanner",
|
|
32
|
+
"sbom",
|
|
33
|
+
"abom",
|
|
34
|
+
"owasp-llm",
|
|
35
|
+
"agentic-remediation",
|
|
36
|
+
"threat-intel",
|
|
37
|
+
"cve",
|
|
38
|
+
"kev",
|
|
39
|
+
"epss",
|
|
40
|
+
"osv",
|
|
41
|
+
"ghsa",
|
|
42
|
+
"cli"
|
|
43
|
+
],
|
|
44
|
+
"author": "Praxis contributors",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18.0.0"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"cli/",
|
|
51
|
+
"!cli/__tests__/",
|
|
52
|
+
"checklists/",
|
|
53
|
+
"configs/",
|
|
54
|
+
"snippets/",
|
|
55
|
+
"ai-defense/",
|
|
56
|
+
"docs/",
|
|
57
|
+
"!docs/internal/",
|
|
58
|
+
"assets/",
|
|
59
|
+
"scripts/check-determinism.mjs",
|
|
60
|
+
"README.md",
|
|
61
|
+
"LICENSE"
|
|
62
|
+
],
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"chalk": "^5.3.0",
|
|
65
|
+
"commander": "^12.1.0",
|
|
66
|
+
"fast-glob": "^3.3.3",
|
|
67
|
+
"ora": "^8.0.1",
|
|
68
|
+
"write-file-atomic": "^5.0.1"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"eslint": "^9.18.0"
|
|
72
|
+
},
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "git+https://github.com/Ganron007/Praxis.git"
|
|
76
|
+
},
|
|
77
|
+
"homepage": "https://github.com/Ganron007/Praxis#readme",
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/Ganron007/Praxis/issues"
|
|
80
|
+
}
|
|
81
|
+
}
|