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
@@ -17,7 +17,7 @@
17
17
 
18
18
  import fs from 'fs';
19
19
  import path from 'path';
20
- import fg from 'fast-glob';
20
+ import fg from '../core/glob.js';
21
21
  import { SKIP_DIRS, SKIP_EXTENSIONS, SKIP_FILENAMES, MAX_FILE_SIZE, MAX_SCAN_FILES, loadGitignorePatterns } from '../utils/patterns.js';
22
22
 
23
23
  // =============================================================================
@@ -27,7 +27,7 @@
27
27
 
28
28
  import fs from 'fs';
29
29
  import path from 'path';
30
- import fg from 'fast-glob';
30
+ import fg from '../core/glob.js';
31
31
  import { BaseAgent, createFinding } from './base-agent.js';
32
32
 
33
33
  // =============================================================================
@@ -33,6 +33,7 @@ import { buildScanFingerprint, fingerprintLine } from '../utils/scan-fingerprint
33
33
  import { readFixLedger, summarizeFixLedger, isReversible } from '../utils/fix-ledger.js';
34
34
  import { readScoreHistory, summarizeHistory } from '../utils/score-history.js';
35
35
  import { toolVersion } from '../core/version.js';
36
+ import { displayPath } from '../core/paths.js';
36
37
 
37
38
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
38
39
  const PKG_VERSION = toolVersion();
@@ -44,18 +45,17 @@ export class HTMLReporter {
44
45
  }
45
46
 
46
47
  normalizePath(filePath, rootPath) {
47
- if (!filePath) return '';
48
- const norm = String(filePath).replace(/\\/g, '/');
49
- const normRoot = rootPath ? String(rootPath).replace(/\\/g, '/') : '';
50
- if (normRoot && norm.startsWith(normRoot)) {
51
- return norm.slice(normRoot.length).replace(/^\/+/, '');
52
- }
53
- return norm.replace(/^[a-zA-Z]:\/+/, '').replace(/^.*\/Praxis\/showcase-target\//, '').replace(/^.*\/showcase-target\//, '');
48
+ // Paths reach the reporter already normalised by the orchestrator, so this only
49
+ // has to fall back for callers that build findings themselves. The strippers
50
+ // that used to live here leaked the username into every HTML report and
51
+ // truncated any real path containing a directory called `Praxis` — see
52
+ // cli/core/paths.js.
53
+ return displayPath(filePath, rootPath);
54
54
  }
55
55
 
56
56
  /**
57
57
  * The provenance line printed in report footers: exactly which tool, runtime and
58
- * vendored data assets produced this document (P-IMP-053). A surprising result should
58
+ * vendored data assets produced this document. A surprising result should
59
59
  * be attributable, not mysterious.
60
60
  */
61
61
  getFingerprintLine(filesScanned = null) {
@@ -880,7 +880,7 @@ function toggleDetail(id) {
880
880
  /**
881
881
  * Score trend over time, from `.praxis/history.json`.
882
882
  *
883
- * This is the part that must not overstate (P-IMP-055). An empty graph reads as
883
+ * This is the part that must not overstate. An empty graph reads as
884
884
  * "flat, no change", which is a different claim from "we have no data", so:
885
885
  * - no prior scans → say the project is at its baseline
886
886
  * - fewer than 3 measurements → say a trend needs more, and show what exists
@@ -985,7 +985,7 @@ function toggleDetail(id) {
985
985
  }
986
986
 
987
987
  /**
988
- * Remediation Ledger — the applied half of the find→fix→verify loop (P-IMP-054).
988
+ * Remediation Ledger — the applied half of the find→fix→verify loop.
989
989
  *
990
990
  * Reads `.praxis/fixes.jsonl` (currently applied changes) and `.praxis/failures.jsonl`
991
991
  * (plans proposed and rejected). Rejections are the more telling half: a rejected fix
@@ -123,11 +123,11 @@ export function buildOrchestrator() {
123
123
  }
124
124
 
125
125
  /**
126
- * Async build — loads built-in agents + any plugins from .praxis/agents/.
126
+ * Async build — loads built-ins and, with explicit trust, .praxis/agents/ plugins.
127
127
  * Preferred over buildOrchestrator() when rootPath is available.
128
128
  *
129
129
  * @param {string} rootPath — project root (for plugin discovery)
130
- * @param {object} options — { verbose, quiet }
130
+ * @param {object} options — { verbose, quiet, trustPlugins }
131
131
  */
132
132
  export async function buildOrchestratorAsync(rootPath, options = {}) {
133
133
  const orchestrator = new OrchestratorClass();
@@ -23,7 +23,14 @@ export const PATTERNS = [
23
23
  {
24
24
  rule: 'SQL_INJECTION_TEMPLATE_LITERAL',
25
25
  title: 'SQL Injection via Template Literal',
26
- regex: /`(?:SELECT|INSERT|UPDATE|DELETE|DROP\s+TABLE|ALTER\s+TABLE|TRUNCATE|CREATE|REPLACE|MERGE)[^`]*\$\{/gi,
26
+ // Each keyword is anchored to the token SQL requires after it. The previous
27
+ // `(?:SELECT|INSERT|...|CREATE|REPLACE|MERGE)[^`]*\$\{` matched the bare
28
+ // keywords, so English words that merely start with one — created, updated,
29
+ // deleted, inserted, replaced — fired a critical on any template literal like
30
+ // `` `created file changed since fix: ${file.path}` ``. Anchoring also lets a
31
+ // word that IS a keyword stand in for SQL only with its real continuation
32
+ // (`MERGE INTO`, `SELECT ... FROM`), not `merge conflict`.
33
+ regex: /`(?:SELECT\s+[\w*`,\s]{1,80}?\b(?:FROM|INTO|SET|VALUES|WHERE)\b|INSERT\s+INTO|UPDATE\s+[\w*`,\s]{1,80}?\bSET\b|DELETE\s+FROM|(?:DROP|ALTER)\s+TABLE|TRUNCATE\s+(?:TABLE\b|(?=[A-Za-z_]*\$\{))|CREATE\s+(?:TABLE|INDEX|UNIQUE|OR\s+REPLACE)|REPLACE\s+INTO|MERGE\s+INTO)[^`]*\$\{/gi,
27
34
  severity: 'critical',
28
35
  cwe: 'CWE-89',
29
36
  owasp: 'A03:2021',