periapsis 0.0.0 → 1.0.1

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/README.md CHANGED
@@ -1,50 +1,290 @@
1
- # Periapsis (draft)
1
+ # Periapsis
2
2
 
3
- License compliance radar for Node dependencies. Reads `package-lock.json` + installed `node_modules`, emits a full SBOM JSON, and fails CI when licenses violate your allowlist. Supports exceptions and traces upstream chains for each violation.
3
+ License compliance gate for Node.js dependencies. Periapsis reads `package-lock.json` + installed `node_modules`, writes an SBOM, and fails CI when dependency licenses are not covered by active policy.
4
4
 
5
5
  ## Install / Run
6
6
 
7
- Local clone:
8
-
9
7
  ```sh
10
- npm install # only needed if you add dependencies later; currently none
11
- npx periapsis --allowed ../allowed-licenses.example.json --violations-out ../sbom-violations.json
8
+ npm install
9
+ npx periapsis --violations-out sbom-violations.json
12
10
  ```
13
11
 
14
- When published to npm (recommended):
12
+ Initialize governed policy files:
15
13
 
16
14
  ```sh
17
- npx periapsis --allowed .sbom-allowlist.json --violations-out sbom-violations.json
15
+ npx periapsis init --preset strict
18
16
  ```
19
17
 
20
- Initialize an allowlist based on SPDX categories:
18
+ `init` now asks which dependency types should be checked by default (unless provided via flags).
21
19
 
22
- ```sh
23
- npx periapsis init --preset strict
24
- ```
20
+ ## Commands
21
+
22
+ - `periapsis`: run SBOM + license gate
23
+ - `periapsis init --preset <strict|standard|permissive> [--policy-dir policy] [--force]`
24
+ - `periapsis exceptions add [--policy-dir policy]` (interactive)
25
+ - `periapsis licenses allow add [--policy-dir policy]` (interactive)
26
+ - `periapsis policy migrate [--from allowedConfig.json] [--policy-dir policy] [--force]`
27
+
28
+ Automation mode:
29
+
30
+ - `periapsis exceptions add --non-interactive ...`
31
+ - `periapsis licenses allow add --non-interactive ...`
32
+ - `periapsis --dep-types dependencies,peerDependencies`
33
+ - `periapsis --production-only` (same as `--dep-types dependencies`)
34
+
35
+ ## Policy Files
36
+
37
+ Periapsis now uses governed policy metadata files under `policy/`:
38
+
39
+ - `policy/policy.json`
40
+ - `policy/licenses.json`
41
+ - `policy/exceptions.json`
42
+
43
+ How they work together:
44
+
45
+ - `policy/policy.json`: global behavior and category fallback policy.
46
+ - `policy/licenses.json`: explicit license allow records with audit metadata and expiration.
47
+ - `policy/exceptions.json`: package-level overrides when a dependency cannot be covered by license policy alone.
25
48
 
26
- ## Options
49
+ Load behavior:
27
50
 
28
- - `--root <path>`: project root (defaults to `cwd`)
29
- - `--lock <file>`: lockfile (default `package-lock.json`)
30
- - `--out <file>`: SBOM JSON output (default `sbom-licenses.json`)
31
- - `--allowed <file>`: allowlist JSON
32
- - `--violations-out <file>`: write violations JSON (includes upstream chains)
33
- - `--quiet`: suppress summary output
34
- - `init --preset <level>`: write `allowedConfig.json` (strict | standard | permissive)
35
- - `init --force`: overwrite existing `allowedConfig.json`
51
+ - Periapsis prefers `policy/` files.
52
+ - If `policy/policy.json` is missing, it can temporarily fall back to legacy `allowedConfig.json` (with warning).
53
+ - Use `periapsis policy migrate` to move legacy config into governed policy files.
36
54
 
37
- Allowlist file schema:
55
+ Validation behavior:
56
+
57
+ - All `policy/*.json` files are schema-validated on load.
58
+ - `licenses add` and `exceptions add` validate the full policy bundle before writing.
59
+ - Invalid files fail fast with field-level schema error messages.
60
+
61
+ ### `policy/policy.json`
38
62
 
39
63
  ```json
40
64
  {
41
- "allowedCategories": ["A", "B"],
42
- "allowedLicenses": ["MIT", "Apache-2.0"],
43
- "exceptions": ["package-name", "name@1.2.3"]
65
+ "allowedCategories": [
66
+ "Permissive Licenses",
67
+ "Weak Copyleft Licenses"
68
+ ],
69
+ "failOnUnknownLicense": true,
70
+ "timezone": "America/Edmonton",
71
+ "dependencyTypes": [
72
+ "dependencies",
73
+ "devDependencies",
74
+ "peerDependencies",
75
+ "optionalDependencies",
76
+ "bundledDependencies"
77
+ ]
44
78
  }
45
79
  ```
46
80
 
47
- ## GitHub Action (example)
81
+ `dependencyTypes` controls which package groups are checked by default:
82
+
83
+ - `dependencies`: runtime production packages
84
+ - `devDependencies`: development/test/build packages
85
+ - `peerDependencies`: host-provided/shared dependencies
86
+ - `optionalDependencies`: non-critical optional packages
87
+ - `bundledDependencies`: dependencies bundled with the package tarball
88
+
89
+ ### `policy/licenses.json`
90
+
91
+ ```json
92
+ [
93
+ {
94
+ "identifier": "MIT",
95
+ "category": "Permissive Licenses",
96
+ "fullName": "MIT License",
97
+ "notes": "Default permissive license.",
98
+ "rationale": "Permissive, low legal risk for SaaS distribution.",
99
+ "approvedBy": ["security"],
100
+ "approvedAt": "2026-02-13T18:00:00Z",
101
+ "expiresAt": null,
102
+ "evidenceRef": "JIRA-1234"
103
+ }
104
+ ]
105
+ ```
106
+
107
+ ### `policy/exceptions.json`
108
+
109
+ ```json
110
+ [
111
+ {
112
+ "package": "@pdftron/pdfjs-express-viewer",
113
+ "scope": { "type": "exact", "version": "8.7.5" },
114
+ "detectedLicenses": ["SEE LICENSE IN LICENSE"],
115
+ "reason": "Commercial dependency required for PDF rendering.",
116
+ "notes": "Revisit annually.",
117
+ "approvedBy": ["legal", "security"],
118
+ "approvedAt": "2026-02-13T18:00:00Z",
119
+ "expiresAt": "2026-08-13T00:00:00Z",
120
+ "evidenceRef": "JIRA-5678"
121
+ }
122
+ ]
123
+ ```
124
+
125
+ Practical authoring rules:
126
+
127
+ - Keep `package` as package name only (for example `caniuse-lite`), and encode version logic in `scope`.
128
+ - Prefer `scope.type = "exact"` for least risk; use `range` carefully; avoid `any` unless necessary.
129
+ - Use non-empty `evidenceRef` values that link to a ticket, issue, or approval artifact.
130
+ - Do not delete old records to “update” policy; add follow-up records so history stays audit-friendly.
131
+
132
+ ## License Categories
133
+
134
+ Periapsis uses three license policy categories:
135
+
136
+ Disclaimer: This section provides operational guidance for engineering policy decisions and is not legal advice. Consult qualified legal counsel for binding interpretation.
137
+
138
+ ### `Permissive Licenses`
139
+
140
+ Examples:
141
+
142
+ - `MIT`
143
+ - `BSD`
144
+ - `Apache-2.0`
145
+
146
+ These generally allow:
147
+
148
+ - Commercial use
149
+ - Modification
150
+ - Distribution
151
+
152
+ With minimal obligations, usually attribution.
153
+
154
+ Typical risk level for most SMEs: Low.
155
+
156
+ ### `Weak Copyleft Licenses`
157
+
158
+ Examples:
159
+
160
+ - `LGPL`
161
+
162
+ These typically require:
163
+
164
+ - Sharing modifications to the licensed component
165
+ - Following specific distribution rules
166
+
167
+ Typical risk level: Moderate, depending on usage and distribution model.
168
+
169
+ ### `Strong Copyleft Licenses`
170
+
171
+ Examples:
172
+
173
+ - `GPL`
174
+ - `AGPL`
175
+
176
+ These may require:
177
+
178
+ - Distribution of source code when software is distributed
179
+ - Sharing modifications
180
+ - Careful handling to avoid proprietary code exposure
181
+
182
+ Typical risk level: High in some commercial contexts.
183
+
184
+ ## Interactive Governance Workflows
185
+
186
+ ### Add an exception
187
+
188
+ ```sh
189
+ npx periapsis exceptions add
190
+ ```
191
+
192
+ Prompts include:
193
+
194
+ - package
195
+ - scope (`exact`, `range`, `any`)
196
+ - detected license identifiers / expression
197
+ - reason (required, multiline)
198
+ - notes (optional)
199
+ - approvedBy (required, comma-separated)
200
+ - approvedAt (default now)
201
+ - expiresAt (`ISO datetime` or `never`)
202
+ - evidenceRef (required)
203
+
204
+ If same package+scope exists, Periapsis prompts to add a follow-up record (recommended) or edit the latest record.
205
+
206
+ ### Add an allowed license
207
+
208
+ ```sh
209
+ npx periapsis licenses allow add
210
+ ```
211
+
212
+ Prompts include:
213
+
214
+ - SPDX identifier (warns if unknown to local SPDX catalog)
215
+ - fullName (optional, auto-filled when known)
216
+ - notes
217
+ - approvedBy
218
+ - approvedAt
219
+ - expiresAt (`ISO datetime` or `never`)
220
+ - category (`Permissive Licenses`, `Weak Copyleft Licenses`, `Strong Copyleft Licenses`, or `Uncategorized / Needs Review`)
221
+ - rationale
222
+ - evidenceRef
223
+
224
+ If identifier already exists, Periapsis appends a follow-up record.
225
+
226
+ ### Non-interactive examples
227
+
228
+ Add allowed license without prompts:
229
+
230
+ ```sh
231
+ npx periapsis licenses allow add \
232
+ --non-interactive \
233
+ --identifier MIT \
234
+ --approved-by security,legal \
235
+ --category "Permissive Licenses" \
236
+ --rationale "Approved baseline license" \
237
+ --evidence-ref JIRA-1234
238
+ ```
239
+
240
+ Add exception without prompts:
241
+
242
+ ```sh
243
+ npx periapsis exceptions add \
244
+ --non-interactive \
245
+ --package caniuse-lite \
246
+ --scope-type exact \
247
+ --version 1.0.30001767 \
248
+ --reason "Reviewed and accepted by security" \
249
+ --approved-by security \
250
+ --expires-at 2027-02-13T00:00:00.000Z \
251
+ --evidence-ref JIRA-5678
252
+ ```
253
+
254
+ Run checker against only production dependencies:
255
+
256
+ ```sh
257
+ npx periapsis --production-only
258
+ ```
259
+
260
+ Run checker against a custom dependency set:
261
+
262
+ ```sh
263
+ npx periapsis --dep-types dependencies,peerDependencies
264
+ ```
265
+
266
+ ## Expiration and Follow-up Behavior
267
+
268
+ A policy record is active when:
269
+
270
+ - `expiresAt` is `null`, or
271
+ - `expiresAt` is later than current time (UTC comparison)
272
+
273
+ Evaluation rules:
274
+
275
+ - If an active explicit license record exists for an SPDX identifier, that license is allowed (record category is metadata and does not gate the decision).
276
+ - If no explicit license record exists, SPDX category fallback uses `policy.allowedCategories`.
277
+ - SPDX expressions are parsed structurally (for example `MIT OR Apache-2.0`) before evaluating allow rules.
278
+ - If only expired records match and no active follow-up exists, this is a violation.
279
+ - Exceptions support `exact`, `range` (semver), and `any` scopes.
280
+ - If a violation is covered by an active exception, gate passes for that package.
281
+ - If only expired exception records match and no active follow-up exists, gate fails.
282
+
283
+ Violation messages include expired record details and remediation commands.
284
+
285
+ ## CI / GitHub Actions
286
+
287
+ Example:
48
288
 
49
289
  ```yaml
50
290
  - uses: actions/checkout@v4
@@ -53,13 +293,48 @@ Allowlist file schema:
53
293
  node-version: 20
54
294
  cache: npm
55
295
  - run: npm ci
56
- - run: npx periapsis --allowed .sbom-allowlist.json --violations-out sbom-violations.json
57
- - run: |
58
- node -e "const fs=require('fs');const p='sbom-violations.json';if(!fs.existsSync(p))process.exit(1);const d=JSON.parse(fs.readFileSync(p,'utf8'));const c=Array.isArray(d)?d.length:Array.isArray(d.violations)?d.violations.length:0;if(c>0){console.error(`Found ${c} license violations`);process.exit(1);}console.log('No license violations');"
296
+ - run: npx periapsis --violations-out sbom-violations.json
59
297
  ```
60
298
 
61
- ## Publish checklist
299
+ When violations exist, Periapsis exits non-zero and prints deterministic markdown summary suitable for Actions logs.
300
+
301
+ ## Troubleshooting Large Violation Sets
302
+
303
+ When you get a large burst of violations, use this order to reduce noise quickly:
304
+
305
+ 1. Confirm you are running the expected CLI version.
306
+ 2. Confirm policy files are being read from the expected repo/path.
307
+ 3. Group by `Type` in the output and fix one class at a time.
308
+
309
+ Quick checks:
310
+
311
+ - If many rows show `license-not-allowed`, add explicit records via `periapsis licenses allow add` for the most common licenses first (`MIT`, `Apache-2.0`, `ISC`, `BSD-3-Clause`).
312
+ - If many rows show `expired-license-policy` or `expired-exception`, add follow-up records instead of editing/deleting old records.
313
+ - If one package appears repeatedly blocked across versions, prefer a targeted exception with `scope.type = "range"` or `exact`.
314
+ - If unknown license expressions are noisy and expected, decide whether to keep strict mode or set `failOnUnknownLicense` to `false` in `policy/policy.json`.
315
+ - If a command fails with schema validation errors, fix the specific field path reported, then rerun.
316
+ - If violations are mostly from test/build tooling, start with `--production-only`, then expand scope incrementally.
317
+
318
+ Recommended triage workflow:
319
+
320
+ 1. Run `npx periapsis --violations-out sbom-violations.json`.
321
+ 2. Count by license in `sbom-licenses.json` and prioritize highest-frequency licenses.
322
+ 3. Add 1-3 high-impact explicit license records.
323
+ 4. Re-run and verify violation count drops.
324
+ 5. Add narrowly scoped exceptions only for true outliers.
325
+
326
+ Team process tips:
327
+
328
+ - Treat `licenses.json` as strategic policy (broad impact) and `exceptions.json` as tactical policy (narrow impact).
329
+ - Require CODEOWNERS/legal-security review for policy edits.
330
+ - Add expirations intentionally, then renew with follow-up records before they expire.
331
+
332
+ ## Governance Recommendation
333
+
334
+ Protect policy changes with CODEOWNERS review:
335
+
336
+ ```txt
337
+ /policy/* @security-team @legal-team
338
+ ```
62
339
 
63
- - Update `package.json` name/version, remove `"private": true` when ready.
64
- - Add CI (lint/test) and changelog.
65
- - Tag a release and publish to npm: `npm publish`.
340
+ Use expiring entries plus follow-up records to preserve decision history without overwriting prior approvals.