praxis-sec 1.2.2 → 1.2.4

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 (42) hide show
  1. package/README.md +84 -115
  2. package/ai-defense/cost-protection.md +6 -0
  3. package/ai-defense/llm-security-checklist.md +6 -0
  4. package/ai-defense/system-prompt-armor.md +7 -1
  5. package/checklists/launch-day.md +6 -7
  6. package/cli/agents/agent-telemetry-agent.js +2 -0
  7. package/cli/agents/api-fuzzer.js +2 -2
  8. package/cli/agents/git-history-scanner.js +14 -15
  9. package/cli/agents/html-reporter.js +2 -1
  10. package/cli/agents/memory-poisoning-agent.js +1 -5
  11. package/cli/commands/agent-fix.js +3 -1
  12. package/cli/commands/audit.js +1271 -1231
  13. package/cli/commands/autofix.js +32 -13
  14. package/cli/commands/baseline.js +2 -1
  15. package/cli/commands/benchmark.js +2 -1
  16. package/cli/commands/ci.js +7 -4
  17. package/cli/commands/env-audit.js +4 -2
  18. package/cli/commands/fix.js +2 -1
  19. package/cli/commands/mcp.js +52 -50
  20. package/cli/commands/remediate.js +2 -1
  21. package/cli/commands/rotate.js +2 -1
  22. package/cli/commands/scan-mcp.js +20 -9
  23. package/cli/commands/scan.js +15 -7
  24. package/cli/commands/score.js +2 -1
  25. package/cli/commands/vibe-check.js +2 -1
  26. package/cli/commands/watch.js +2 -1
  27. package/cli/core/glob.js +7 -5
  28. package/cli/core/paths.js +4 -4
  29. package/cli/core/web/jobs.js +2 -0
  30. package/cli/data/documented-secret-examples.json +14 -0
  31. package/cli/utils/entropy.js +19 -0
  32. package/cli/utils/hermes-tool-registry.js +11 -9
  33. package/configs/firebase/security-checklist.md +3 -3
  34. package/configs/supabase/security-checklist.md +19 -21
  35. package/docs/RELEASE-1.2.4.md +85 -0
  36. package/docs/RELEASING.md +51 -0
  37. package/docs/THIRD_PARTY_NOTICES.md +8 -0
  38. package/docs/THREAT_INTEL.md +4 -2
  39. package/docs/USAGE.md +97 -76
  40. package/package.json +82 -81
  41. package/snippets/README.md +6 -0
  42. package/snippets/auth/jwt-checklist.md +14 -13
@@ -30,7 +30,7 @@ import {
30
30
  MAX_FILE_SIZE,
31
31
  loadGitignorePatterns
32
32
  } from '../utils/patterns.js';
33
- import { isHighEntropyMatch, getConfidence } from '../utils/entropy.js';
33
+ import { isHighEntropyMatch, getConfidence, isDocumentedSecretExample } from '../utils/entropy.js';
34
34
  import fg from '../core/glob.js';
35
35
 
36
36
  // =============================================================================
@@ -135,6 +135,7 @@ export async function vibeCheckCommand(targetPath = '.', options = {}) {
135
135
  pattern.pattern.lastIndex = 0;
136
136
  let match;
137
137
  while ((match = pattern.pattern.exec(line)) !== null) {
138
+ if (isDocumentedSecretExample(pattern.name, match[0])) continue;
138
139
  if (pattern.requiresEntropyCheck && !isHighEntropyMatch(match[0])) continue;
139
140
  secretFindings.push({
140
141
  file, line: lineNum + 1, column: match.index + 1,
@@ -16,7 +16,7 @@ import chalk from 'chalk';
16
16
  import { displayPath } from '../core/paths.js';
17
17
  import { execFileSync } from 'child_process';
18
18
  import { SKIP_DIRS, SKIP_EXTENSIONS, SKIP_FILENAMES, SECRET_PATTERNS, SECURITY_PATTERNS } from '../utils/patterns.js';
19
- import { isHighEntropyMatch, getConfidence } from '../utils/entropy.js';
19
+ import { isHighEntropyMatch, getConfidence, isDocumentedSecretExample } from '../utils/entropy.js';
20
20
  import * as output from '../utils/output.js';
21
21
  import { ScoringEngine } from '../agents/scoring-engine.js';
22
22
 
@@ -175,6 +175,7 @@ function scanFile(filePath, patterns) {
175
175
  pattern.pattern.lastIndex = 0;
176
176
  let match;
177
177
  while ((match = pattern.pattern.exec(line)) !== null) {
178
+ if (isDocumentedSecretExample(pattern.name, match[0])) continue;
178
179
  if (pattern.requiresEntropyCheck && !isHighEntropyMatch(match[0])) continue;
179
180
  findings.push({
180
181
  line: i + 1,
package/cli/core/glob.js CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * Scanner file discovery boundary. Target-controlled patterns are bounded before
3
- * fast-glob/braces parse them (GHSA-vfj7-8cjw-p6xm has no upstream patch yet).
3
+ * the glob parser sees them, including pathological ignore patterns.
4
4
  * Directory symlinks must not turn a project scan into a scan of the host.
5
5
  */
6
- import fastGlob from 'fast-glob';
6
+ import { glob as discover, globSync } from 'tinyglobby';
7
+ import { validateDir } from './fs.js';
7
8
 
8
9
  const MAX_PATTERNS = 4096;
9
10
  const MAX_LENGTH = 8192;
@@ -42,15 +43,16 @@ export function validateGlobPatterns(patterns) {
42
43
  }
43
44
 
44
45
  function safeOptions(patterns, options) {
46
+ if (options.cwd && !validateDir(options.cwd, { exitOnMissing: false })) throw new Error('Scan root must be an existing directory');
45
47
  validateGlobPatterns(patterns);
46
48
  if (options.ignore) validateGlobPatterns(options.ignore);
47
- return { ...options, followSymbolicLinks: false };
49
+ return { ...options, expandDirectories: false, followSymbolicLinks: false };
48
50
  }
49
51
 
50
52
  function glob(patterns, options = {}) {
51
- return fastGlob(patterns, safeOptions(patterns, options));
53
+ return discover(patterns, safeOptions(patterns, options));
52
54
  }
53
55
 
54
- glob.sync = (patterns, options = {}) => fastGlob.sync(patterns, safeOptions(patterns, options));
56
+ glob.sync = (patterns, options = {}) => globSync(patterns, safeOptions(patterns, options));
55
57
 
56
58
  export default glob;
package/cli/core/paths.js CHANGED
@@ -35,7 +35,7 @@ const toSlash = (p) => String(p).split(path.sep).join('/').replace(/\\/g, '/');
35
35
  // normally come from the host's own glob, but a path read back from a report or
36
36
  // a cache can carry the other platform's shape, and treating it as relative would
37
37
  // print a drive letter straight into the output. Same guard sarif.js uses.
38
- const isAbsoluteLike = (s) => path.isAbsolute(s) || /^[a-zA-Z]:[\\/]/.test(s);
38
+ const isAbsoluteLike = (s) => path.isAbsolute(s) || /^[a-zA-Z]:[\\/]/.test(s) || /^\\\\/.test(s);
39
39
 
40
40
  /**
41
41
  * Render a finding's file for display.
@@ -58,7 +58,7 @@ export function displayPath(file, root) {
58
58
  if (root) {
59
59
  const rel = path.relative(root, s);
60
60
  // `rel` starting with '..' means the file is outside the scan root.
61
- if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) return toSlash(rel);
61
+ if (rel && rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel)) return toSlash(rel);
62
62
  }
63
63
 
64
64
  const home = os.homedir();
@@ -68,7 +68,7 @@ export function displayPath(file, root) {
68
68
 
69
69
  // Outside the root and outside home: identify the file without publishing the
70
70
  // local directory layout.
71
- return path.basename(s);
71
+ return path.posix.basename(toSlash(s));
72
72
  }
73
73
 
74
74
  /**
@@ -88,4 +88,4 @@ export function normalizeFindingPaths(findings, root) {
88
88
  return findings;
89
89
  }
90
90
 
91
- export default displayPath;
91
+ export default displayPath;
@@ -172,6 +172,8 @@ export async function runScanWithOrchestrator(rootPath, onProgress) {
172
172
  },
173
173
  });
174
174
 
175
+ if (agentResults.some(agent => !agent.success)) throw new Error('Scan incomplete: one or more agents failed');
176
+
175
177
  let score = 100;
176
178
  let grade = 'A';
177
179
  let categories = {};
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": 1,
3
+ "lastReviewed": "2026-10-07",
4
+ "description": "Exact provider-documented non-working credential identifiers. No substring, test-directory, or history-wide exemptions.",
5
+ "examples": [
6
+ {
7
+ "pattern": "AWS Access Key ID",
8
+ "aliases": ["AGENT_LOG_EXPOSED_AWS_KEY"],
9
+ "value": "AKIAIOSFODNN7EXAMPLE",
10
+ "source": "https://docs.aws.amazon.com/AmazonS3/latest/developerguide/RESTAuthentication.html",
11
+ "reason": "AWS explicitly labels this identifier a non-working example credential."
12
+ }
13
+ ]
14
+ }
@@ -19,6 +19,25 @@
19
19
  * Patterns with known prefixes (sk-ant-, ghp_, AKIA...) are already precise enough.
20
20
  */
21
21
 
22
+ import fs from 'fs';
23
+
24
+ const documentedExamples = new Map();
25
+ try {
26
+ const catalog = JSON.parse(fs.readFileSync(new URL('../data/documented-secret-examples.json', import.meta.url), 'utf8'));
27
+ for (const entry of catalog.examples || []) {
28
+ if (typeof entry.pattern !== 'string' || typeof entry.value !== 'string') continue;
29
+ for (const name of [entry.pattern, ...(Array.isArray(entry.aliases) ? entry.aliases : [])]) {
30
+ if (typeof name !== 'string') continue;
31
+ if (!documentedExamples.has(name)) documentedExamples.set(name, new Set());
32
+ documentedExamples.get(name).add(entry.value);
33
+ }
34
+ }
35
+ } catch { /* Missing example data must never suppress unknown credentials. */ }
36
+
37
+ export function isDocumentedSecretExample(patternName, matched) {
38
+ return documentedExamples.get(patternName)?.has(extractSecretValue(matched)) === true;
39
+ }
40
+
22
41
  // =============================================================================
23
42
  // ENTROPY CALCULATION
24
43
  // =============================================================================
@@ -62,7 +62,9 @@ export const HERMES_TOOLS = [
62
62
  },
63
63
  handler: async ({ path: scanPath, severity = 'medium', deep = false }) => {
64
64
  const { auditCommand } = await import('../commands/audit.js');
65
- return auditCommand(scanPath, { severity, deep, json: true, quiet: true });
65
+ const report = await auditCommand(scanPath, { deep, _agenticInner: true, deps: false, noAi: true });
66
+ const ranks = { critical: 4, high: 3, medium: 2, low: 1 };
67
+ return { ...report, findings: report.findings.filter(f => (ranks[f.severity] ?? 0) >= (ranks[severity] ?? 0)) };
66
68
  },
67
69
  },
68
70
 
@@ -86,7 +88,7 @@ export const HERMES_TOOLS = [
86
88
  },
87
89
  handler: async ({ target }) => {
88
90
  const { scanMcpCommand } = await import('../commands/scan-mcp.js');
89
- return scanMcpCommand(target, { json: true });
91
+ return scanMcpCommand(target, { json: true, quiet: true });
90
92
  },
91
93
  },
92
94
 
@@ -115,7 +117,7 @@ export const HERMES_TOOLS = [
115
117
  },
116
118
  handler: async ({ path: projectPath, severity = 'medium' }) => {
117
119
  const { mcpGetFindings } = await import('../commands/mcp.js');
118
- return mcpGetFindings({ projectPath, severity });
120
+ return mcpGetFindings({ reportPath: path.join(projectPath, '.praxis', 'last-report.json'), severity });
119
121
  },
120
122
  },
121
123
 
@@ -123,7 +125,7 @@ export const HERMES_TOOLS = [
123
125
  name: 'praxis_suppress_finding',
124
126
  description:
125
127
  'Suppress a known-safe finding by inserting an inline praxis-ignore comment ' +
126
- 'in the source file before the flagged line. Use only when the finding is a ' +
128
+ 'on the flagged source line. Use only when the finding is a ' +
127
129
  'confirmed false positive and you can document why it is safe.',
128
130
  parameters: {
129
131
  type: 'object',
@@ -184,11 +186,11 @@ export const HERMES_TOOLS = [
184
186
  // =============================================================================
185
187
 
186
188
  const KNOWN_HASHES = {
187
- praxis_audit: '4d282d29e44fcc01',
188
- praxis_scan_mcp: 'f967aea9626ca840',
189
- praxis_get_findings: 'c09c9447efd574b3',
190
- praxis_suppress_finding: '3b7339419fe52ac7',
191
- praxis_memory_list: 'c71c996716d1805b',
189
+ praxis_audit: '4bdb0dafe1efea27',
190
+ praxis_scan_mcp: '99c278579ccc7e38',
191
+ praxis_get_findings: '8c3b10c970f047ab',
192
+ praxis_suppress_finding: '3cedaa6e7ae974ae',
193
+ praxis_memory_list: '9d405b87e81c7263',
192
194
  };
193
195
 
194
196
  function toolHash(tool) {
@@ -24,11 +24,11 @@ allow read, write: if request.time < timestamp.date(2024, 12, 31);
24
24
  ### 2. [ ] Firestore rules require authentication
25
25
 
26
26
  ```javascript
27
- // GOOD: Requires authentication
27
+ // Authentication check alone: insufficient for private per-user data
28
28
  allow read, write: if request.auth != null;
29
29
 
30
30
  // BETTER: Requires authentication AND ownership
31
- allow read, write: if request.auth.uid == userId;
31
+ allow read, write: if request.auth != null && request.auth.uid == userId;
32
32
  ```
33
33
 
34
34
  ### 3. [ ] Storage rules have file type validation
@@ -49,7 +49,7 @@ allow write: if request.resource.size < 5 * 1024 * 1024;
49
49
  ### 5. [ ] Default deny rule at the end
50
50
 
51
51
  ```javascript
52
- // Catch-all: deny everything not explicitly allowed
52
+ // Explicit default deny. Matching allow rules are additive; this does not override another allow.
53
53
  match /{document=**} {
54
54
  allow read, write: if false;
55
55
  }
@@ -2,42 +2,40 @@
2
2
 
3
3
  **Complete this checklist before launching your Supabase-powered app.**
4
4
 
5
- Based on [CVE-2025-48757](https://byteiota.com/supabase-security-flaw-170-apps-exposed-by-missing-rls/) and common pentesting findings.
5
+ Follow [Supabase RLS guidance](https://supabase.com/docs/guides/database/postgres/row-level-security). Test allow and deny behavior for each operation and role; these examples are not proof of access isolation.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Critical: Row Level Security (RLS)
10
10
 
11
- ### 1. [ ] RLS is ENABLED on ALL tables
11
+ ### 1. [ ] RLS is enabled on tables exposed to application users
12
12
 
13
13
  ```sql
14
14
  -- Check which tables DON'T have RLS
15
- SELECT schemaname, tablename
16
- FROM pg_tables
17
- WHERE schemaname = 'public'
18
- AND tablename NOT IN (
19
- SELECT tablename::text FROM pg_class
20
- WHERE relrowsecurity = true
21
- );
15
+ SELECT n.nspname AS schemaname, c.relname AS tablename
16
+ FROM pg_class AS c
17
+ JOIN pg_namespace AS n ON n.oid = c.relnamespace
18
+ WHERE n.nspname = 'public'
19
+ AND c.relkind IN ('r', 'p')
20
+ AND NOT c.relrowsecurity;
22
21
  ```
23
22
 
24
- **If any tables appear, enable RLS immediately:**
23
+ **Review exposed tables and enable RLS with tested policies and grants:**
25
24
  ```sql
26
25
  ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
27
26
  ```
28
27
 
29
- ### 2. [ ] Every table has at least one policy
28
+ ### 2. [ ] Exposed tables have the policies needed for intended operations
30
29
 
31
30
  ```sql
32
- -- Tables with RLS enabled but NO policies (locked to everyone!)
33
- SELECT tablename FROM pg_tables
34
- WHERE schemaname = 'public'
35
- AND tablename IN (
36
- SELECT tablename::text FROM pg_class WHERE relrowsecurity = true
37
- )
38
- AND tablename NOT IN (
39
- SELECT tablename FROM pg_policies
40
- );
31
+ -- RLS tables with no policies: ordinary roles are denied, but bypass roles differ.
32
+ SELECT n.nspname AS schemaname, c.relname AS tablename
33
+ FROM pg_class AS c
34
+ JOIN pg_namespace AS n ON n.oid = c.relnamespace
35
+ WHERE n.nspname = 'public'
36
+ AND c.relkind IN ('r', 'p')
37
+ AND c.relrowsecurity
38
+ AND NOT EXISTS (SELECT 1 FROM pg_policy AS p WHERE p.polrelid = c.oid);
41
39
  ```
42
40
 
43
41
  ### 3. [ ] Policies use `auth.uid()` not hardcoded values
@@ -97,7 +95,7 @@ grep -r "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" ./src
97
95
  The `anon` key is designed to be public, but only if RLS is properly configured.
98
96
 
99
97
  ```typescript
100
- // Frontend: Use anon key (safe if RLS is set up)
98
+ // Frontend: use the appropriate public key with tested RLS policies and grants
101
99
  const supabase = createClient(url, anonKey);
102
100
 
103
101
  // Server: Use service_role key (for admin operations)
@@ -0,0 +1,85 @@
1
+ # Praxis 1.2.4
2
+
3
+ This patch corrects cases where a scan could report success without examining the
4
+ requested target, and fixes project targeting in the older annotation loop.
5
+
6
+ - Full, secret, and CI scans require a directory. A file supplied as a scan root
7
+ now produces an error instead of a false empty result. The VS Code current-file
8
+ action scans its workspace and selects diagnostics for the requested file.
9
+ - Full-scan JSON includes `scanComplete`, `scanErrors`, and `dependencyAudit`.
10
+ Failed discovery, failed agents, unavailable dependency audits, or failed
11
+ requested legal scans mark the result incomplete and exit unsuccessfully.
12
+ Findings alone still do not fail a normal scan; use `scan ci` for severity gates.
13
+ - Incomplete scans do not update scan cache, score history, or the playbook, and
14
+ cannot start the annotation loop or satisfy fix verification. HTML reports show
15
+ an incomplete warning; the web runner refuses failed-agent results.
16
+ A failed annotation-loop verification also overrides `--fail-below`; JSON
17
+ reports the failure once without inner-scan progress text. Reports are generated
18
+ after verification so HTML and machine-readable status agree.
19
+ - `scan full --timeout` now reaches the orchestrator.
20
+ - Annotation targets resolve inside the requested project, including symlink
21
+ destinations. Protected scanner/Git paths and unsupported comment formats such
22
+ as JSON are rejected. Writes are atomic, and unchanged annotations are not
23
+ counted as applied. Multiple annotations in one file are inserted from the
24
+ bottom to preserve finding line numbers; replaying the same report is safe.
25
+ - In-root directories beginning with two dots retain their full relative path.
26
+ Foreign Windows backslash and UNC paths fall back to a safe basename.
27
+ - Memory-poisoning document discovery respects ignore rules and dependency
28
+ directories instead of reading ignored nested artifacts as project context.
29
+ - File discovery uses `tinyglobby` in place of the vulnerable
30
+ `fast-glob → micromatch → braces` chain. Directory expansion stays disabled,
31
+ brace limits and symlink boundaries remain enforced, and discovery has tests
32
+ for hidden files, exclusions, extglobs, literal directories, and async/sync parity.
33
+ - Exact provider-documented non-working credentials are recognized from an
34
+ attributed data catalog. Unknown credentials on the same line still produce
35
+ findings. History scans process all matches, distinguish credentials that share
36
+ the same masked display, and report operational failures as incomplete.
37
+ - Upload rules require request/upload filename evidence instead of treating
38
+ local package metadata or any variable named `filename` as a vulnerable upload.
39
+ - The independently versioned VS Code extension is prepared as 1.0.1. CLI calls
40
+ use executable/argument arrays instead of shell text, and default npx execution
41
+ forbids automatic package installation. Report fields are escaped, severity
42
+ classes are sanitized, and a Content-Security-Policy blocks remote content.
43
+ Windows npm launchers resolve to the CLI's JavaScript entry point, preserving
44
+ literal arguments without invoking a command shell.
45
+
46
+ - MCP suppression now writes on the matched line, preserves line endings, rejects
47
+ unsupported formats and non-integer lines, and writes atomically. MCP repository
48
+ scans use the current scoring API, expose agent failures as incomplete, and
49
+ reject file roots.
50
+ - Hermes handlers return structured audit/manifest results, read the explicit saved
51
+ report path, apply severity filtering, and register against corrected integrity
52
+ hashes. Default Hermes audits disclose skipped dependency checks.
53
+ - Documentation uses canonical commands, preserves errors, corrects CI examples,
54
+ describes data egress and score limitations, and provides a release procedure.
55
+ The Claude Code plugin instructions are independently versioned as 3.0.1.
56
+
57
+ ## Validation and limitations
58
+
59
+ Release validation covers the CLI test suite on Node 18, 20, 22 and 24, lint,
60
+ extension compilation and runtime tests, scan determinism, a complete zero-critical
61
+ self-scan, local PR Action base/head comparison, and installed-package smoke tests.
62
+ The production dependency chain was removed rather than overridden; the reviewed
63
+ full dependency audit, including development dependencies, reports zero advisories.
64
+ Audit results reflect the advisory database at the time of verification.
65
+
66
+ AWS explicitly identifies the formerly reported fixture credential as
67
+ [non-working example data](https://docs.aws.amazon.com/AmazonS3/latest/developerguide/RESTAuthentication.html).
68
+ Only that exact identifier in AWS credential rules is exempted; tests retain
69
+ unknown credentials on the same line. History and test files are not excluded.
70
+
71
+ Noncritical heuristic findings and existing lint warnings remain. A self-scan
72
+ score is not a measurement of scanner accuracy. Live LLM provider behavior and
73
+ interactive VS Code UI behavior were not validated by these release checks.
74
+
75
+ ## Distribution
76
+
77
+ The GitHub release, immutable `v1.2.4` tag, and floating Marketplace Action `v1`
78
+ are prepared from the same commit after its hosted CI succeeds. The release
79
+ includes the npm package tarball and a SHA-256 checksum. **npm publication is a
80
+ separate maintainer step**; a GitHub release does not change npm latest.
81
+
82
+ Use `Ganron007/Praxis@v1.2.4` to pin the Action. For CLI use before npm publication,
83
+ install the attached tarball or run from a checkout of this tag. After publication,
84
+ install `praxis-sec@1.2.4` from npm. See [the release procedure](RELEASING.md) for
85
+ the checks and publishing handoff.
@@ -0,0 +1,51 @@
1
+ # Releasing Praxis
2
+
3
+ The CLI/npm package, GitHub Action, Claude Code plugin, and VS Code extension
4
+ have separate distribution surfaces. A GitHub release does not publish npm or
5
+ the VS Code extension.
6
+
7
+ ## Prepare
8
+
9
+ 1. Choose the CLI patch version and update `package.json`, both root lockfile
10
+ version fields, and `docs/RELEASE-<version>.md`.
11
+ 2. Update public examples to the intended Action tag. Keep independently
12
+ versioned integrations consistent with their own manifests and lockfiles.
13
+ 3. Run `npm ci` and `npm run release:check`. This checks CLI tests, lint,
14
+ production dependency advisories, scan determinism, a complete zero-critical
15
+ self-scan, editor compilation/runtime tests, package exclusions, and an
16
+ installed-package scan with redaction and cache parity.
17
+ 4. Review `git diff --check` and the package contents. Keep `docs/internal/`,
18
+ runtime state, credentials, test fixtures, and local binaries out of Git and
19
+ the npm package.
20
+
21
+ ## GitHub and Marketplace
22
+
23
+ 1. Commit the complete release and push it. Wait for CI to pass on that exact
24
+ commit, including all four Node versions, determinism, package/editor checks,
25
+ and the local Action smoke test.
26
+ 2. Create an annotated immutable version tag, such as `v1.2.4`, on that commit.
27
+ Never replace a previously published version tag with different source.
28
+ 3. Pack the committed source with `npm pack`. Attach the tarball and its SHA-256
29
+ checksum to the GitHub release, using the reviewed release notes.
30
+ 4. Advance the floating `v1` Action tag deliberately to the same commit. Check
31
+ the prior remote value first and protect the update against concurrent changes.
32
+ 5. Verify the release, version tag, floating tag, assets, and successful CI all
33
+ refer to the reviewed source. Consumers can use the exact version tag or
34
+ commit instead of the floating tag.
35
+
36
+ ## npm publication
37
+
38
+ The maintainer publishes npm separately from the same clean, committed checkout:
39
+
40
+ ```bash
41
+ git checkout v1.2.4
42
+ npm ci
43
+ npm publish
44
+ ```
45
+
46
+ `prepublishOnly` reruns the release gates and rejects an uncommitted working
47
+ tree or a version different from the committed package metadata. Authentication
48
+ and npm account requirements must be satisfied by the publishing maintainer.
49
+ After publication, confirm `npm view praxis-sec version dist-tags` and install
50
+ that exact registry version in a clean project. GitHub and npm may legitimately
51
+ show different versions until this step completes.
@@ -17,6 +17,14 @@ and attribution as required.
17
17
  - **Source:** https://github.com/0x4D31/endpoint-ai-agent-abuse (v0.1.0)
18
18
  - **License:** CC0-1.0 (public domain dedication) — https://creativecommons.org/publicdomain/zero/1.0/
19
19
 
20
+ ## Documented non-working credential examples
21
+
22
+ - **File:** `cli/data/documented-secret-examples.json`
23
+ - **Source:** [AWS S3 authentication examples](https://docs.aws.amazon.com/AmazonS3/latest/developerguide/RESTAuthentication.html)
24
+ - Contains one exact identifier AWS labels non-working, with our own annotations.
25
+ No documentation prose is reproduced. This is factual example provenance, not
26
+ permission to suppress arbitrary credentials, test files, or repository history.
27
+
20
28
  ## Standards referenced (not vendored)
21
29
 
22
30
  Findings are mapped to the following frameworks; their text is not reproduced:
@@ -12,7 +12,9 @@ For everything else (general CLI usage, scanning, hooks, etc.), see the main
12
12
 
13
13
  ## 1. Commands
14
14
 
15
- Run these from any directory — they only touch `~/.praxis/` (your home dir).
15
+ Feed-update commands store caches under `~/.praxis/`. Scan commands also read
16
+ the selected project and can update its local Praxis state. Updates contact
17
+ configured remote sources; cached data is not a guarantee of current coverage.
16
18
 
17
19
  | Command | What it does |
18
20
  | --- | --- |
@@ -35,7 +37,7 @@ over no data.
35
37
  praxis intel update
36
38
  ```
37
39
 
38
- Expected output:
40
+ Illustrative output (versions and counts depend on the fetched feeds):
39
41
 
40
42
  ```
41
43
  Fetching sources...