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.
Files changed (60) hide show
  1. package/assets/praxis-architecture.svg +304 -0
  2. package/assets/praxis-logo.svg +38 -0
  3. package/cli/agents/abom-generator.js +1 -1
  4. package/cli/agents/agent-attestation-agent.js +10 -1
  5. package/cli/agents/agent-config-scanner.js +1 -1
  6. package/cli/agents/ai-infra-inventory-agent.js +482 -482
  7. package/cli/agents/base-agent.js +1 -1
  8. package/cli/agents/endpoint-agent-abuse-agent.js +1 -1
  9. package/cli/agents/html-reporter.js +10 -10
  10. package/cli/agents/index.js +2 -2
  11. package/cli/agents/injection-tester.js +8 -1
  12. package/cli/agents/mcp-security-agent.js +600 -594
  13. package/cli/agents/memory-poisoning-agent.js +1 -1
  14. package/cli/agents/model-file-scanner.js +1 -1
  15. package/cli/agents/orchestrator.js +375 -355
  16. package/cli/agents/prompt-injection-prober.js +228 -228
  17. package/cli/bin/praxis.js +7 -3
  18. package/cli/commands/agent-fix.js +1091 -1245
  19. package/cli/commands/audit.js +1232 -1216
  20. package/cli/commands/baseline.js +3 -2
  21. package/cli/commands/benchmark.js +1 -1
  22. package/cli/commands/ci.js +50 -25
  23. package/cli/commands/deps.js +11 -5
  24. package/cli/commands/diff.js +2 -1
  25. package/cli/commands/env-audit.js +1 -1
  26. package/cli/commands/fix.js +1 -1
  27. package/cli/commands/legal.js +2 -1
  28. package/cli/commands/mcp.js +3 -2
  29. package/cli/commands/openclaw.js +3 -6
  30. package/cli/commands/red-team.js +351 -350
  31. package/cli/commands/remediate.js +1 -1
  32. package/cli/commands/rotate.js +1 -1
  33. package/cli/commands/rules.js +1 -1
  34. package/cli/commands/scan-standard.js +3 -6
  35. package/cli/commands/scan.js +554 -554
  36. package/cli/commands/score.js +1 -1
  37. package/cli/commands/undo.js +22 -77
  38. package/cli/commands/vibe-check.js +3 -2
  39. package/cli/commands/watch.js +6 -5
  40. package/cli/core/fix-plan.js +274 -0
  41. package/cli/core/fs.js +27 -0
  42. package/cli/core/git-clone.js +8 -6
  43. package/cli/core/glob.js +56 -0
  44. package/cli/core/output/html-theme.js +158 -158
  45. package/cli/core/output/json.js +56 -48
  46. package/cli/core/output/sarif.js +2 -2
  47. package/cli/core/paths.js +91 -0
  48. package/cli/core/web/jobs.js +2 -2
  49. package/cli/core/web/server.js +19 -8
  50. package/cli/data/threatpacks/latest.json +41 -41
  51. package/cli/integrations/github-action.js +136 -0
  52. package/cli/utils/cache-manager.js +2 -1
  53. package/cli/utils/plugin-loader.js +15 -95
  54. package/cli/utils/rule-import.js +227 -227
  55. package/cli/utils/rule-registry.js +425 -425
  56. package/cli/utils/scan-fingerprint.js +1 -1
  57. package/cli/utils/score-history.js +118 -118
  58. package/docs/USAGE.md +16 -9
  59. package/docs/design/WEB-UI.md +4 -5
  60. 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 (P-IMP-053): during self-dogfooding a scan reported 325 findings
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 (P-IMP-055):
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, auto-discovered when running scans from a project
900
- that contains them. Scaffold one with:
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.0.0 · node v24.14.1 · probes v1.1(23) · threatpack v1.1(3) · eaa v0.1.0 · files 197
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 discovers plugins from `.praxis/agents/` automatically when scans run
1099
- from that project root. The discovery uses `buildOrchestratorAsync(rootPath)` —
1100
- the synchronous `buildOrchestrator()` skips plugin loading.
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 | Make sure you're running through `buildOrchestratorAsync(rootPath)` — the default `praxis scan` does this; programmatic API callers using `buildOrchestrator()` won't get plugins. |
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. |
@@ -1,7 +1,6 @@
1
1
  # Praxis Web UI — design & threat model
2
2
 
3
- > **Status:** design agreed; v1 implemented read-only (scan orchestration).
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**, honouring `AGENTS.md`'s
25
- no-build-step rule: no bundler, no framework, no network fetch of assets. Reports use
26
- the shared `cli/core/output/html-theme.js`.
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.0",
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
- "keywords": [
17
- "security",
18
- "praxis",
19
- "ai-security",
20
- "agentic-security",
21
- "llm-security",
22
- "mcp-security",
23
- "prompt-injection",
24
- "supply-chain",
25
- "secrets",
26
- "scanner",
27
- "sast",
28
- "devsecops",
29
- "red-team",
30
- "vulnerability-scanner",
31
- "sbom",
32
- "abom",
33
- "owasp-llm",
34
- "agentic-remediation",
35
- "threat-intel",
36
- "cve",
37
- "kev",
38
- "epss",
39
- "osv",
40
- "ghsa",
41
- "cli"
42
- ],
43
- "author": "Praxis contributors",
44
- "license": "MIT",
45
- "engines": {
46
- "node": ">=18.0.0"
47
- },
48
- "files": [
49
- "cli/",
50
- "!cli/__tests__/",
51
- "checklists/",
52
- "configs/",
53
- "snippets/",
54
- "ai-defense/",
55
- "docs/",
56
- "!docs/internal/",
57
- "scripts/check-determinism.mjs",
58
- "README.md",
59
- "LICENSE"
60
- ],
61
- "dependencies": {
62
- "chalk": "^5.3.0",
63
- "commander": "^12.1.0",
64
- "fast-glob": "^3.3.3",
65
- "ora": "^8.0.1",
66
- "write-file-atomic": "^7.0.0"
67
- },
68
- "devDependencies": {
69
- "eslint": "^9.18.0"
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
+ }