praxis-sec 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 +21 -0
- package/README.md +170 -0
- package/ai-defense/cost-protection.md +292 -0
- package/ai-defense/llm-security-checklist.md +324 -0
- package/ai-defense/prompt-injection-patterns.js +283 -0
- package/ai-defense/system-prompt-armor.md +327 -0
- package/checklists/launch-day.md +168 -0
- package/cli/agents/abom-generator.js +225 -0
- package/cli/agents/agent-attestation-agent.js +318 -0
- package/cli/agents/agent-config-scanner.js +787 -0
- package/cli/agents/agent-telemetry-agent.js +415 -0
- package/cli/agents/agentic-security-agent.js +296 -0
- package/cli/agents/agentic-supply-chain-agent.js +463 -0
- package/cli/agents/ai-infra-inventory-agent.js +449 -0
- package/cli/agents/api-fuzzer.js +345 -0
- package/cli/agents/auth-bypass-agent.js +348 -0
- package/cli/agents/base-agent.js +280 -0
- package/cli/agents/cicd-scanner.js +300 -0
- package/cli/agents/config-auditor.js +757 -0
- package/cli/agents/deep-analyzer.js +776 -0
- package/cli/agents/endpoint-agent-abuse-agent.js +404 -0
- package/cli/agents/exception-handler-agent.js +187 -0
- package/cli/agents/git-history-scanner.js +169 -0
- package/cli/agents/governance-audits.js +138 -0
- package/cli/agents/hermes-security-agent.js +536 -0
- package/cli/agents/html-reporter.js +1125 -0
- package/cli/agents/index.js +147 -0
- package/cli/agents/injection-tester.js +502 -0
- package/cli/agents/legal-risk-agent.js +328 -0
- package/cli/agents/llm-redteam.js +199 -0
- package/cli/agents/managed-agent-scanner.js +333 -0
- package/cli/agents/mcp-security-agent.js +588 -0
- package/cli/agents/memory-poisoning-agent.js +305 -0
- package/cli/agents/mobile-scanner.js +231 -0
- package/cli/agents/model-file-scanner.js +259 -0
- package/cli/agents/orchestrator.js +355 -0
- package/cli/agents/pii-compliance-agent.js +301 -0
- package/cli/agents/policy-engine.js +229 -0
- package/cli/agents/prompt-injection-prober.js +224 -0
- package/cli/agents/rag-security-agent.js +204 -0
- package/cli/agents/recon-agent.js +207 -0
- package/cli/agents/sbom-generator.js +265 -0
- package/cli/agents/scoring-engine.js +273 -0
- package/cli/agents/ssrf-prober.js +130 -0
- package/cli/agents/stateful-watcher.js +238 -0
- package/cli/agents/supabase-rls-agent.js +154 -0
- package/cli/agents/supply-chain-agent.js +857 -0
- package/cli/agents/swarm-orchestrator.js +200 -0
- package/cli/agents/verifier-agent.js +303 -0
- package/cli/agents/vibe-coding-agent.js +250 -0
- package/cli/bin/praxis.js +866 -0
- package/cli/commands/abom.js +73 -0
- package/cli/commands/agent-fix.js +1245 -0
- package/cli/commands/audit.js +1180 -0
- package/cli/commands/autofix.js +383 -0
- package/cli/commands/baseline.js +193 -0
- package/cli/commands/benchmark.js +327 -0
- package/cli/commands/checklist.js +223 -0
- package/cli/commands/ci.js +403 -0
- package/cli/commands/deps.js +516 -0
- package/cli/commands/diff.js +200 -0
- package/cli/commands/doctor.js +195 -0
- package/cli/commands/env-audit.js +349 -0
- package/cli/commands/fix.js +218 -0
- package/cli/commands/guard.js +396 -0
- package/cli/commands/hooks.js +278 -0
- package/cli/commands/init.js +514 -0
- package/cli/commands/legal.js +158 -0
- package/cli/commands/live-advisories.js +241 -0
- package/cli/commands/mcp.js +660 -0
- package/cli/commands/openclaw.js +386 -0
- package/cli/commands/red-team.js +350 -0
- package/cli/commands/redteam.js +78 -0
- package/cli/commands/remediate.js +797 -0
- package/cli/commands/rotate.js +768 -0
- package/cli/commands/rules.js +196 -0
- package/cli/commands/scan-mcp.js +534 -0
- package/cli/commands/scan-skill.js +588 -0
- package/cli/commands/scan-standard.js +251 -0
- package/cli/commands/scan.js +524 -0
- package/cli/commands/score.js +449 -0
- package/cli/commands/shell.js +514 -0
- package/cli/commands/team-report.js +398 -0
- package/cli/commands/undo.js +161 -0
- package/cli/commands/update-intel.js +126 -0
- package/cli/commands/vibe-check.js +276 -0
- package/cli/commands/watch.js +757 -0
- package/cli/commands/web.js +63 -0
- package/cli/core/ast/guardrail-detector.js +141 -0
- package/cli/core/ast/index.js +22 -0
- package/cli/core/ast/parser.js +676 -0
- package/cli/core/ast/scope-tree.js +287 -0
- package/cli/core/ast/taint-tracker.js +158 -0
- package/cli/core/branding.js +37 -0
- package/cli/core/env.js +38 -0
- package/cli/core/errors.js +61 -0
- package/cli/core/fs.js +62 -0
- package/cli/core/output/compliance.js +90 -0
- package/cli/core/output/html-theme.js +158 -0
- package/cli/core/output/index.js +57 -0
- package/cli/core/output/json.js +48 -0
- package/cli/core/output/sarif.js +240 -0
- package/cli/core/version.js +67 -0
- package/cli/core/web/jobs.js +183 -0
- package/cli/core/web/projects.js +146 -0
- package/cli/core/web/server.js +439 -0
- package/cli/data/atlas-knowledge.json +5640 -0
- package/cli/data/eaa-catalog.json +39 -0
- package/cli/data/known-mcps.json +26 -0
- package/cli/data/probes/prompt-injection-corpus.json +271 -0
- package/cli/data/threat-intel.json +85 -0
- package/cli/data/threatpacks/latest.json +41 -0
- package/cli/hooks/patterns.js +313 -0
- package/cli/hooks/post-tool-use.js +140 -0
- package/cli/hooks/pre-tool-use.js +186 -0
- package/cli/index.js +90 -0
- package/cli/providers/llm-provider.js +766 -0
- package/cli/utils/autofix-rules.js +74 -0
- package/cli/utils/cache-manager.js +310 -0
- package/cli/utils/compliance-map.js +191 -0
- package/cli/utils/entropy.js +132 -0
- package/cli/utils/fix-ledger.js +127 -0
- package/cli/utils/hermes-tool-registry.js +252 -0
- package/cli/utils/intel/cache.js +61 -0
- package/cli/utils/intel/http.js +88 -0
- package/cli/utils/intel/index.js +235 -0
- package/cli/utils/intel/merge.js +229 -0
- package/cli/utils/intel/sources/epss.js +54 -0
- package/cli/utils/intel/sources/ghsa.js +81 -0
- package/cli/utils/intel/sources/gitguardian.js +40 -0
- package/cli/utils/intel/sources/gitleaks.js +101 -0
- package/cli/utils/intel/sources/kev.js +38 -0
- package/cli/utils/intel/sources/nvd.js +84 -0
- package/cli/utils/intel/sources/osv.js +132 -0
- package/cli/utils/intel/sources/phylum.js +44 -0
- package/cli/utils/intel/sources/snyk.js +46 -0
- package/cli/utils/intel/sources/socket.js +69 -0
- package/cli/utils/intel/sources/sonatype.js +84 -0
- package/cli/utils/intel/sources/threatpack.js +69 -0
- package/cli/utils/mcp-trust.js +60 -0
- package/cli/utils/output.js +251 -0
- package/cli/utils/patterns.js +1130 -0
- package/cli/utils/pdf-generator.js +94 -0
- package/cli/utils/plugin-loader.js +364 -0
- package/cli/utils/rule-import.js +228 -0
- package/cli/utils/rule-registry.js +426 -0
- package/cli/utils/scan-fingerprint.js +109 -0
- package/cli/utils/scan-playbook.js +312 -0
- package/cli/utils/score-history.js +119 -0
- package/cli/utils/secrets-verifier.js +247 -0
- package/cli/utils/security-memory.js +296 -0
- package/cli/utils/standards/atlas-knowledge.js +87 -0
- package/cli/utils/standards/index.js +127 -0
- package/cli/utils/standards/sources/avid.js +45 -0
- package/cli/utils/standards/sources/eu-ai-act.js +89 -0
- package/cli/utils/standards/sources/google-saif.js +39 -0
- package/cli/utils/standards/sources/iso-42001.js +94 -0
- package/cli/utils/standards/sources/mitre-atlas.js +54 -0
- package/cli/utils/standards/sources/nist-ai-600-1.js +45 -0
- package/cli/utils/standards/sources/owasp-llm.js +45 -0
- package/cli/utils/standards/sources/owasp-ml.js +45 -0
- package/cli/utils/threat-intel.js +265 -0
- package/configs/firebase/firestore-rules.txt +215 -0
- package/configs/firebase/security-checklist.md +236 -0
- package/configs/firebase/storage-rules.txt +206 -0
- package/configs/gitignore-template +258 -0
- package/configs/nextjs-security-headers.js +220 -0
- package/configs/praxisignore-template +50 -0
- package/configs/supabase/secure-client.ts +225 -0
- package/configs/supabase/security-checklist.md +278 -0
- package/docs/THIRD_PARTY_NOTICES.md +26 -0
- package/docs/THREAT_INTEL.md +292 -0
- package/docs/USAGE.md +1205 -0
- package/docs/design/WEB-UI.md +82 -0
- package/package.json +71 -0
- package/scripts/check-determinism.mjs +119 -0
- package/snippets/README.md +122 -0
- package/snippets/api-security/api-security-checklist.md +412 -0
- package/snippets/api-security/cors-config.ts +322 -0
- package/snippets/api-security/input-validation.ts +430 -0
- package/snippets/auth/jwt-checklist.md +322 -0
- package/snippets/rate-limiting/nextjs-middleware.ts +211 -0
- package/snippets/rate-limiting/upstash-ratelimit.ts +229 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Praxis Web UI — design & threat model
|
|
2
|
+
|
|
3
|
+
> **Status:** design agreed; v1 implemented read-only (scan orchestration).
|
|
4
|
+
> Feature tracking: `P-IMP-052` in `docs/internal/IMPROVEMENT-PLAN.md`.
|
|
5
|
+
>
|
|
6
|
+
> This document exists because a web UI for Praxis is the one place where adding a
|
|
7
|
+
> feature means adding an **attack surface** to a security tool. A CLI runs with the
|
|
8
|
+
> privileges of the person who typed the command. A server runs with whatever
|
|
9
|
+
> privileges the person who *connected to it* has — and may have more.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. What it is
|
|
14
|
+
|
|
15
|
+
A local web app for driving scans and managing scan projects:
|
|
16
|
+
|
|
17
|
+
- register projects (directories to scan)
|
|
18
|
+
- run single or concurrent scans
|
|
19
|
+
- watch live progress
|
|
20
|
+
- browse findings, agents, standards, and the fix ledger
|
|
21
|
+
- manage baselines
|
|
22
|
+
|
|
23
|
+
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`.
|
|
27
|
+
|
|
28
|
+
## 2. Threat model — the part that matters
|
|
29
|
+
|
|
30
|
+
Praxis reads source code, prints code excerpts in findings, and can **write code**
|
|
31
|
+
(`praxis fix` applies LLM-guided patches). Exposing that over HTTP is remote code
|
|
32
|
+
execution by design if done carelessly. Threats, ranked:
|
|
33
|
+
|
|
34
|
+
| # | Threat | Why it is serious | Mitigation in v1 |
|
|
35
|
+
|---|--------|-------------------|------------------|
|
|
36
|
+
| T1 | **Fix application over the network** | An endpoint that accepts a path and writes a patch is arbitrary code write on the host. Even "verified" patches are LLM-authored. | **Not implemented.** v1 is read-only. Remediation stays in the CLI, where a human sees the diff. |
|
|
37
|
+
| T2 | **Arbitrary filesystem read via project paths** | A client-supplied path lets a caller scan `/`, read `~/.ssh`, or enumerate the host. | Projects are **registered explicitly by the operator** and stored server-side. The client sends a project *id*, never a path. Paths are resolved once at registration and pinned. |
|
|
38
|
+
| T3 | **Finding data leaks other projects** | Findings contain file paths and source excerpts. A multi-project UI that serves them to any caller leaks the whole workspace. | Every API call is scoped to a registered project id. No endpoint enumerates arbitrary paths. Loopback-only by default. |
|
|
39
|
+
| T4 | **Remote binding without auth** | `0.0.0.0` with no auth exposes everything above to the network. | **Loopback-only by default.** A non-loopback bind requires an explicit `--allow-remote` flag **and** refuses to start unless a token is provided; the token is required in a header. |
|
|
40
|
+
| T5 | **CSRF / DNS rebinding from a browser** | A malicious page can POST to `127.0.0.1:PORT` while the victim has the UI open. | All mutating endpoints require a custom header (`X-Praxis-Client`) that a cross-origin form cannot set, plus `Origin` checking. This is the standard localhost-server defence. |
|
|
41
|
+
| T6 | **Path traversal in served assets** | Serving the frontend from disk invites `../../`. | Frontend is generated in memory from the shared theme. No static file serving. |
|
|
42
|
+
| T7 | **Resource exhaustion** | 28 agents × many projects; a scan queue can be flooded. | Bounded concurrency, a max project count, and per-request limits. |
|
|
43
|
+
| T8 | **Scan data egress** | `--deep` sends code to an LLM provider. | The UI does **not** enable `--deep`; LLM-backed analysis stays a CLI opt-in so egress is always an explicit human decision. |
|
|
44
|
+
|
|
45
|
+
### Explicit non-goals for v1
|
|
46
|
+
|
|
47
|
+
- No authentication, no multi-user, no tenancy (it binds to loopback).
|
|
48
|
+
- No fix application, no `--agentic`, no `--deep`.
|
|
49
|
+
- No remote/multi-tenant deployment story. Exposing this to a network is out of scope
|
|
50
|
+
until there is real auth, and shipping that is a separate, larger piece of work.
|
|
51
|
+
|
|
52
|
+
## 3. Shape
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
cli/commands/web.js command wiring, bind/guard logic
|
|
56
|
+
cli/core/web/
|
|
57
|
+
server.js node:http server, routing, guards
|
|
58
|
+
projects.js project registry (pinned, resolved paths)
|
|
59
|
+
jobs.js bounded job queue driving Orchestrator
|
|
60
|
+
api.js JSON API handlers
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Storage under `.praxis/web/` in the * Praxis home* (`~/.praxis/web`), not inside a
|
|
64
|
+
scanned project, so scanning a project never reads the UI's own state.
|
|
65
|
+
|
|
66
|
+
Endpoints (all project-scoped by id):
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
GET /api/projects list registered projects
|
|
70
|
+
POST /api/projects register (operator-supplied path, server-side)
|
|
71
|
+
POST /api/projects/:id/scan enqueue a scan
|
|
72
|
+
GET /api/jobs job states
|
|
73
|
+
GET /api/jobs/:id/events SSE progress stream
|
|
74
|
+
GET /api/projects/:id/report full JSON report (reuses existing renderer)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 4. Honest limitations
|
|
78
|
+
|
|
79
|
+
- Scanning still runs as the invoking user; the UI does not sandbox the scan.
|
|
80
|
+
- Loopback-only is the safe default, not a security boundary against a local attacker.
|
|
81
|
+
- Progress is best-effort; the orchestrator reports per-agent completion, not per-file.
|
|
82
|
+
- No incremental/streaming findings yet — the report renders after the scan finishes.
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "praxis-sec",
|
|
3
|
+
"version": "1.0.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",
|
|
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
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Determinism gate (P-IMP-053).
|
|
4
|
+
*
|
|
5
|
+
* Scans a target twice and compares the *detection* identities produced by each run.
|
|
6
|
+
* A scanner that returns different findings for an unchanged tree is not a scanner you
|
|
7
|
+
* can baseline, gate CI on, or quote a number from — so this asserts the property
|
|
8
|
+
* directly instead of hoping for it.
|
|
9
|
+
*
|
|
10
|
+
* Only detection identity (`file:rule`) is compared, deliberately excluding severity,
|
|
11
|
+
* line numbers and messages: a change in scoring or message wording is not a change in
|
|
12
|
+
* what Praxis found, and failing on it would make the gate noise rather than signal.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node scripts/check-determinism.mjs [target] [--runs=2] [--timeout=60000]
|
|
16
|
+
*
|
|
17
|
+
* Exit codes:
|
|
18
|
+
* 0 deterministic (identical detection identities across runs)
|
|
19
|
+
* 1 drift detected — the diff is printed
|
|
20
|
+
* 2 the scan itself could not be run
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { spawnSync } from 'child_process';
|
|
24
|
+
import path from 'path';
|
|
25
|
+
import { fileURLToPath } from 'url';
|
|
26
|
+
// Comparison semantics come from the library, not a copy: the helper that CI relies on
|
|
27
|
+
// must be the same one the unit tests cover.
|
|
28
|
+
import { diffFindings } from '../cli/utils/scan-fingerprint.js';
|
|
29
|
+
|
|
30
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
31
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
32
|
+
const CLI = path.join(REPO_ROOT, 'cli', 'bin', 'praxis.js');
|
|
33
|
+
|
|
34
|
+
const argv = process.argv.slice(2);
|
|
35
|
+
const flag = (name, fallback) => {
|
|
36
|
+
const hit = argv.find(a => a.startsWith(`--${name}=`));
|
|
37
|
+
return hit ? hit.split('=')[1] : fallback;
|
|
38
|
+
};
|
|
39
|
+
const positional = argv.filter(a => !a.startsWith('--'));
|
|
40
|
+
const target = positional[0] || '.';
|
|
41
|
+
const runs = Math.max(2, parseInt(flag('runs', '2'), 10));
|
|
42
|
+
const timeout = parseInt(flag('timeout', '120000'), 10);
|
|
43
|
+
|
|
44
|
+
function scan(label) {
|
|
45
|
+
process.stderr.write(` run ${label}…\n`);
|
|
46
|
+
const res = spawnSync(
|
|
47
|
+
process.execPath,
|
|
48
|
+
[CLI, 'scan', 'full', target, '--no-deps', '--no-cache', '--json', '--timeout', String(timeout)],
|
|
49
|
+
{ encoding: 'utf8', maxBuffer: 256 * 1024 * 1024, cwd: REPO_ROOT }
|
|
50
|
+
);
|
|
51
|
+
if (res.status !== 0) {
|
|
52
|
+
process.stderr.write(` scan failed (exit ${res.status}): ${(res.stderr || '').slice(-500)}\n`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// The CLI pretty-prints the JSON object; take from the first brace onward.
|
|
56
|
+
const start = res.stdout.indexOf('{');
|
|
57
|
+
if (start < 0) {
|
|
58
|
+
process.stderr.write(' scan produced no JSON\n');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(res.stdout.slice(start));
|
|
63
|
+
} catch (err) {
|
|
64
|
+
process.stderr.write(` could not parse scan JSON: ${err.message}\n`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(`Determinism gate — ${runs} runs over "${target}"\n`);
|
|
70
|
+
|
|
71
|
+
const results = [];
|
|
72
|
+
for (let i = 1; i <= runs; i++) {
|
|
73
|
+
const out = scan(`${i}/${runs}`);
|
|
74
|
+
if (!out) process.exit(2);
|
|
75
|
+
results.push(out);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fingerprints first: if these differ, a data or runtime change explains any drift.
|
|
79
|
+
const fingerprints = results.map(r => r.fingerprint ?? null);
|
|
80
|
+
const fpStable = fingerprints.every(f => JSON.stringify(f) === JSON.stringify(fingerprints[0]));
|
|
81
|
+
console.log(`\nFingerprint stable across runs: ${fpStable ? 'yes' : 'NO — inputs differ, drift is expected'}`);
|
|
82
|
+
for (const f of fingerprints) {
|
|
83
|
+
if (!f) continue;
|
|
84
|
+
const d = f.data || {};
|
|
85
|
+
console.log(` praxis ${f.tool ?? '?'} · node ${f.node ?? '?'} · probes v${d.probeCorpus?.version ?? '?'}(${d.probeCorpus?.items ?? '?'}) · threatpack v${d.threatPack?.version ?? '?'}(${d.threatPack?.items ?? '?'}) · files ${f.filesScanned ?? '?'}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const base = results[0].findings || [];
|
|
89
|
+
const baseIdentityCount = new Set(base.map(f => `${f.file ?? '?'}::${f.rule ?? '?'}`)).size;
|
|
90
|
+
let drifted = false;
|
|
91
|
+
|
|
92
|
+
for (let i = 1; i < results.length; i++) {
|
|
93
|
+
const other = results[i].findings || [];
|
|
94
|
+
const { added, removed, common } = diffFindings(base, other);
|
|
95
|
+
const total = baseIdentityCount || 1;
|
|
96
|
+
const drift = Math.round(((added.length + removed.length) / total) * 10000) / 100;
|
|
97
|
+
|
|
98
|
+
console.log(`\nRun 1 vs run ${i + 1}: ${common} shared identities, ${added.length} added, ${removed.length} removed (${drift}% drift)`);
|
|
99
|
+
console.log(` (raw findings: ${base.length} vs ${other.length})`);
|
|
100
|
+
if (added.length) {
|
|
101
|
+
console.log(' + appeared:');
|
|
102
|
+
added.slice(0, 20).forEach(x => console.log(` ${x}`));
|
|
103
|
+
if (added.length > 20) console.log(` …and ${added.length - 20} more`);
|
|
104
|
+
}
|
|
105
|
+
if (removed.length) {
|
|
106
|
+
console.log(' - disappeared:');
|
|
107
|
+
removed.slice(0, 20).forEach(x => console.log(` ${x}`));
|
|
108
|
+
if (removed.length > 20) console.log(` …and ${removed.length - 20} more`);
|
|
109
|
+
}
|
|
110
|
+
if (added.length || removed.length) drifted = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (drifted) {
|
|
114
|
+
console.log('\nFAIL — detection is not deterministic for an unchanged tree.');
|
|
115
|
+
console.log('Detection identities must be stable. Investigate before trusting or baselining these numbers.');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log(`\nPASS — detection identical across ${runs} runs (${baseIdentityCount} distinct identities across ${base.length} raw findings).`);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Security Snippets
|
|
2
|
+
|
|
3
|
+
**Copy-paste code blocks for common security patterns.**
|
|
4
|
+
|
|
5
|
+
This folder contains drop-in code snippets for securing your application. Each snippet is heavily commented to explain *why* it works.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Available Snippets
|
|
10
|
+
|
|
11
|
+
### Rate Limiting
|
|
12
|
+
|
|
13
|
+
| File | Description |
|
|
14
|
+
|------|-------------|
|
|
15
|
+
| [upstash-ratelimit.ts](./rate-limiting/upstash-ratelimit.ts) | Production-ready rate limiting with Upstash Redis. Includes different limiters for API, auth, and AI endpoints. |
|
|
16
|
+
| [nextjs-middleware.ts](./rate-limiting/nextjs-middleware.ts) | In-memory rate limiting at the Next.js middleware level. Good for development or simple deployments. |
|
|
17
|
+
|
|
18
|
+
**Quick Start:**
|
|
19
|
+
```typescript
|
|
20
|
+
import { apiRatelimit } from './rate-limiting/upstash-ratelimit';
|
|
21
|
+
|
|
22
|
+
const { success } = await apiRatelimit.limit(userId);
|
|
23
|
+
if (!success) {
|
|
24
|
+
return new Response('Too Many Requests', { status: 429 });
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### Authentication
|
|
31
|
+
|
|
32
|
+
| File | Description |
|
|
33
|
+
|------|-------------|
|
|
34
|
+
| [jwt-checklist.md](./auth/jwt-checklist.md) | Complete JWT security checklist. Covers algorithms, token lifetime, storage, validation, and revocation. |
|
|
35
|
+
|
|
36
|
+
**Key Points:**
|
|
37
|
+
- Use RS256/ES256, not HS256 with weak secrets
|
|
38
|
+
- Access tokens: 15-60 minutes max
|
|
39
|
+
- Store in httpOnly cookies, not localStorage
|
|
40
|
+
- Always validate issuer and audience claims
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### API Security
|
|
45
|
+
|
|
46
|
+
| File | Description |
|
|
47
|
+
|------|-------------|
|
|
48
|
+
| [cors-config.ts](./api-security/cors-config.ts) | CORS configurations for Next.js, Express, Fastify, Hono, and Vercel Edge. |
|
|
49
|
+
| [input-validation.ts](./api-security/input-validation.ts) | Zod schemas and validation patterns for API endpoints. Includes file upload validation. |
|
|
50
|
+
| [api-security-checklist.md](./api-security/api-security-checklist.md) | Comprehensive API security checklist based on OWASP API Security Top 10. |
|
|
51
|
+
|
|
52
|
+
**Quick Start (CORS):**
|
|
53
|
+
```typescript
|
|
54
|
+
const ALLOWED_ORIGINS = ['https://yourapp.com'];
|
|
55
|
+
|
|
56
|
+
// Only allow specific origins
|
|
57
|
+
if (origin && ALLOWED_ORIGINS.includes(origin)) {
|
|
58
|
+
headers['Access-Control-Allow-Origin'] = origin;
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Quick Start (Validation):**
|
|
63
|
+
```typescript
|
|
64
|
+
import { z } from 'zod';
|
|
65
|
+
|
|
66
|
+
const schema = z.object({
|
|
67
|
+
email: z.string().email().max(255),
|
|
68
|
+
password: z.string().min(8).max(128),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const result = schema.safeParse(body);
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
return Response.json({ error: result.error.issues }, { status: 400 });
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Usage Pattern
|
|
80
|
+
|
|
81
|
+
Each snippet follows this format:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// WHAT: Brief description
|
|
86
|
+
// WHY: What attack/vulnerability this prevents
|
|
87
|
+
// HOW: Integration instructions
|
|
88
|
+
// =============================================================================
|
|
89
|
+
|
|
90
|
+
[actual code with inline comments]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Related Resources
|
|
96
|
+
|
|
97
|
+
- **[/configs](../configs/)** - Framework configs (Next.js headers, Supabase RLS, Firebase rules)
|
|
98
|
+
- **[/ai-defense](../ai-defense/)** - AI/LLM security (prompt injection, cost protection)
|
|
99
|
+
- **[/checklists](../checklists/)** - Security checklists (launch day)
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Contributing
|
|
104
|
+
|
|
105
|
+
Have a security snippet that saved your app? Add it here!
|
|
106
|
+
|
|
107
|
+
1. Create a new file in the appropriate subfolder
|
|
108
|
+
2. Add extensive comments explaining:
|
|
109
|
+
- What attack this prevents
|
|
110
|
+
- How to integrate it
|
|
111
|
+
- Common gotchas
|
|
112
|
+
3. Open a PR
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## What's Next
|
|
117
|
+
|
|
118
|
+
Future additions planned:
|
|
119
|
+
- Webhook signature verification (Stripe, GitHub)
|
|
120
|
+
- OAuth state parameter handling
|
|
121
|
+
- CSRF protection patterns
|
|
122
|
+
- Content Security Policy builder
|