@xenos1996/usa 2.16.1 → 2.18.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cli-help.d.ts","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"AAAA,iGAAiG;AAEjG,eAAO,MAAM,gBAAgB,QAOrB,CAAC;AAET,eAAO,MAAM,cAAc,QAcnB,CAAC;AAET,eAAO,MAAM,IAAI,QAoGT,CAAC;AAET,eAAO,MAAM,iBAAiB,mbAmB7B,CAAC"}
1
+ {"version":3,"file":"cli-help.d.ts","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"AAAA,iGAAiG;AAEjG,eAAO,MAAM,gBAAgB,QAOrB,CAAC;AAET,eAAO,MAAM,cAAc,QAcnB,CAAC;AAET,eAAO,MAAM,IAAI,QA2GT,CAAC;AAET,eAAO,MAAM,iBAAiB,mbAmB7B,CAAC"}
package/dist/cli-help.js CHANGED
@@ -40,6 +40,7 @@ usa — Universal Software Auditor
40
40
  usa foundation init [path] Capture project intent into .usa/foundation.yaml
41
41
  usa foundation show [path] Print the effective intent and asserted facts
42
42
  usa live [path] Conversational audit session (deterministic without a provider)
43
+ usa serve [opts] Localhost audit server (loopback only, bearer token)
43
44
  usa models --provider ID List models the provider advertises
44
45
 
45
46
  evolve options
@@ -81,6 +82,12 @@ live options
81
82
  models options
82
83
  --provider <id> LLM provider preset (or $USA_PROVIDER)
83
84
 
85
+ serve options
86
+ --port <n> Port to listen on (default 0 = ephemeral, printed at startup)
87
+ --token <t> Bearer token for /api/* (default: generated and printed once)
88
+ --allow-root <dir> Auditable root, repeatable (default: working directory)
89
+ --data-dir <dir> Persist finished audits here (default: memory only)
90
+
84
91
  audit options
85
92
  --dry-run Print the report path that would be written and exit
86
93
  --out <file> Report path (default AUDIT.md)
@@ -1 +1 @@
1
- {"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"AAAA,iGAAiG;AAEjG,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;CAO/B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;CAc7B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoGnB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;CAmBhC,CAAC;AAEF,6BAA6B;AAC7B,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,2EAA2E"}
1
+ {"version":3,"file":"cli-help.js","sourceRoot":"","sources":["../src/cli-help.ts"],"names":[],"mappings":"AAAA,iGAAiG;AAEjG,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;CAO/B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;CAc7B,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2GnB,CAAC,IAAI,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;CAmBhC,CAAC;AAEF,6BAA6B;AAC7B,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,2EAA2E"}
@@ -0,0 +1,3 @@
1
+ import { type Args } from './cli-args.js';
2
+ export declare function cmdServe(args: Args): Promise<number>;
3
+ //# sourceMappingURL=cli-serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-serve.d.ts","sourceRoot":"","sources":["../src/cli-serve.ts"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAqCrD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CA+B1D"}
@@ -0,0 +1,60 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomBytes } from 'node:crypto';
4
+ import { startServer } from './serve/server.js';
5
+ import { list, str } from './cli-args.js';
6
+ /** Parse and validate serve flags. Returns an error string instead of throwing. */
7
+ function resolveServeArgs(args) {
8
+ const port = Number(str(args, 'port', '0') ?? '0');
9
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
10
+ return { error: '--port must be an integer 0-65535 (0 picks an ephemeral port)' };
11
+ }
12
+ const roots = list(args, 'allow-root');
13
+ const resolvedRoots = (roots.length > 0 ? roots : [process.cwd()]).map((r) => path.resolve(r));
14
+ for (const r of resolvedRoots) {
15
+ if (!fs.existsSync(r))
16
+ return { error: `--allow-root does not exist: ${r}` };
17
+ }
18
+ const given = str(args, 'token');
19
+ const dataDirRaw = str(args, 'data-dir');
20
+ return {
21
+ port,
22
+ token: given ?? randomBytes(32).toString('hex'),
23
+ tokenGiven: given !== undefined,
24
+ roots: resolvedRoots,
25
+ dataDir: dataDirRaw === undefined ? undefined : path.resolve(dataDirRaw),
26
+ };
27
+ }
28
+ export async function cmdServe(args) {
29
+ const resolved = resolveServeArgs(args);
30
+ if ('error' in resolved) {
31
+ console.error(resolved.error);
32
+ return 2;
33
+ }
34
+ try {
35
+ const { port: bound } = await startServer({
36
+ port: resolved.port,
37
+ token: resolved.token,
38
+ roots: resolved.roots,
39
+ ...(resolved.dataDir === undefined ? {} : { dataDir: resolved.dataDir }),
40
+ });
41
+ console.log(`usa serve: listening on http://127.0.0.1:${bound} (loopback only — never expose directly)`);
42
+ if (resolved.tokenGiven) {
43
+ console.log('usa serve: using the provided bearer token for /api/*.');
44
+ }
45
+ else {
46
+ console.log('usa serve: generated bearer token (printed once, required on /api/*):');
47
+ console.log(resolved.token);
48
+ }
49
+ if (resolved.dataDir !== undefined) {
50
+ console.log(`usa serve: persisting finished audits to ${resolved.dataDir}.`);
51
+ }
52
+ }
53
+ catch (err) {
54
+ console.error(`usa serve: cannot listen: ${err.message}`);
55
+ return 2;
56
+ }
57
+ await new Promise(() => { });
58
+ return 0;
59
+ }
60
+ //# sourceMappingURL=cli-serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-serve.js","sourceRoot":"","sources":["../src/cli-serve.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAkDA,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2B7D;AAID,+EAA+E;AAE/E,iBAAS,YAAY,CACnB,KAAK,GAAE,MAAM,GAAG,SAA2B,EAC3C,OAAO,GAAE,MAAwB,GAChC,OAAO,CAOT;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAoDA,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2B7D;AAID,+EAA+E;AAE/E,iBAAS,YAAY,CACnB,KAAK,GAAE,MAAM,GAAG,SAA2B,EAC3C,OAAO,GAAE,MAAwB,GAChC,OAAO,CAOT;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
package/dist/cli.js CHANGED
@@ -10,6 +10,7 @@ import { cmdFoundation, cmdLive, cmdModels } from './cli-session.js';
10
10
  import { cmdDiff, cmdVerifyReport } from './cli-reports.js';
11
11
  import { cmdBootstrap, cmdInit, cmdLearn } from './cli-scaffold.js';
12
12
  import { cmdEvolve } from './cli-evolve.js';
13
+ import { cmdServe } from './cli-serve.js';
13
14
  /** Dispatch only: every command lives in its own cli-*.ts module. */
14
15
  /* -------------------------------------------------------------------- main -- */
15
16
  const COMMANDS = {
@@ -28,6 +29,7 @@ const COMMANDS = {
28
29
  categories: cmdCategories,
29
30
  live: cmdLive,
30
31
  models: cmdModels,
32
+ serve: cmdServe,
31
33
  };
32
34
  /** Global --help/--version, resolved before dispatch. Returns null to continue. */
33
35
  function handleTopLevelFlags(args, cmd) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import type { AuditReport } from '../types.js';
2
+ import type { MaturityProfile } from '../engine/maturity.js';
3
+ /**
4
+ * Audit job management for `usa serve` (ADR-0039).
5
+ *
6
+ * One running job at a time (a second submit gets 409), bounded history,
7
+ * and targets contained inside explicit allow-roots with the same
8
+ * resolve-and-verify discipline as the object store (SEC-010): `..` and
9
+ * symlink escapes throw instead of serving. The runner is injectable so
10
+ * tests can use slow fakes without running real audits.
11
+ */
12
+ export interface ServeJobOptions {
13
+ target: string;
14
+ depth: string;
15
+ profile: string;
16
+ format: string;
17
+ }
18
+ export type JobStatus = 'running' | 'done' | 'error';
19
+ export interface ServeJob {
20
+ id: string;
21
+ status: JobStatus;
22
+ options: ServeJobOptions;
23
+ createdAt: string;
24
+ finishedAt?: string;
25
+ report?: AuditReport;
26
+ profile?: MaturityProfile;
27
+ /** Rendered report (set for finished jobs when persistence is on). */
28
+ rendered?: {
29
+ contentType: string;
30
+ body: string;
31
+ };
32
+ error?: string;
33
+ }
34
+ /**
35
+ * Resolve a requested target against allow-roots. Throws a clean Error when
36
+ * the target does not exist or escapes every root (after realpath, so
37
+ * symlinks cannot smuggle it out).
38
+ */
39
+ export declare function resolveTarget(raw: string, roots: string[]): string;
40
+ export interface JobRunner {
41
+ (options: ServeJobOptions, target: string): {
42
+ report: AuditReport;
43
+ profile: MaturityProfile;
44
+ } | Promise<{
45
+ report: AuditReport;
46
+ profile: MaturityProfile;
47
+ }>;
48
+ }
49
+ export interface JobHooks {
50
+ /** Fires for every finished job (done or error), after status is set. */
51
+ onDone?: (job: ServeJob) => void;
52
+ }
53
+ export declare function createJobManager(runner: JobRunner, historyLimit?: number, hooks?: JobHooks): {
54
+ submit: (options: ServeJobOptions, target: string) => {
55
+ id: string;
56
+ } | {
57
+ conflict: true;
58
+ };
59
+ get: (id: string) => ServeJob | undefined;
60
+ list: () => ServeJob[];
61
+ restore: (jobs: ServeJob[]) => void;
62
+ };
63
+ /**
64
+ * File persistence for `--data-dir` (ADR-0040): one JSON file per finished
65
+ * job, pruned to the same bound as memory history. Only finished jobs are
66
+ * ever written — a crash leaves no partial record behind.
67
+ */
68
+ export interface PersistedJob {
69
+ id: string;
70
+ status: JobStatus;
71
+ options: ServeJobOptions;
72
+ createdAt: string;
73
+ finishedAt?: string;
74
+ error?: string;
75
+ rendered?: {
76
+ contentType: string;
77
+ body: string;
78
+ };
79
+ }
80
+ export declare function persistJob(dir: string, job: ServeJob): void;
81
+ /**
82
+ * Read back persisted jobs, skipping anything malformed with a loud
83
+ * warning instead of refusing to start — a corrupt file must not take
84
+ * down the server, but it must not pass silently either.
85
+ */
86
+ export declare function loadPersisted(dir: string): ServeJob[];
87
+ /** Prune persisted files past the bound, oldest first (shared rule). */
88
+ export declare function prunePersisted(dir: string, historyLimit: number): void;
89
+ //# sourceMappingURL=jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/serve/jobs.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;;;;;;GAQG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAelE;AAED,MAAM,WAAW,SAAS;IACxB,CACE,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,MAAM,GAEZ;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,eAAe,CAAA;KAAE,GACjD,OAAO,CAAC;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,OAAO,EAAE,eAAe,CAAA;KAAE,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,YAAY,SAAK,EACjB,KAAK,GAAE,QAAa,GACnB;IACD,MAAM,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,KAAK;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1F,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAC;IAC1C,IAAI,EAAE,MAAM,QAAQ,EAAE,CAAC;IACvB,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;CACrC,CA+DA;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAY3D;AAkBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,EAAE,CAmBrD;AAED,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAmBtE"}
@@ -0,0 +1,169 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import fs, { existsSync, realpathSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ /**
5
+ * Resolve a requested target against allow-roots. Throws a clean Error when
6
+ * the target does not exist or escapes every root (after realpath, so
7
+ * symlinks cannot smuggle it out).
8
+ */
9
+ export function resolveTarget(raw, roots) {
10
+ const resolved = path.resolve(raw);
11
+ if (!existsSync(resolved)) {
12
+ throw new Error(`target does not exist: ${raw}`);
13
+ }
14
+ const real = realpathSync(resolved);
15
+ // Roots are realpath'd too: comparing a lexically-resolved root against a
16
+ // real target false-rejects on machines where tmp-style prefixes are
17
+ // symlinks (fail-closed, but noisy). Roots must exist — the server
18
+ // rejects missing ones at startup with a clean message.
19
+ for (const root of roots) {
20
+ const rel = path.relative(realpathSync(path.resolve(root)), real);
21
+ if (rel !== '..' && !rel.startsWith(`..${path.sep}`))
22
+ return real;
23
+ }
24
+ throw new Error(`target escapes the allowed roots: ${raw}`);
25
+ }
26
+ export function createJobManager(runner, historyLimit = 20, hooks = {}) {
27
+ const jobs = new Map();
28
+ let running = false;
29
+ function prune() {
30
+ const done = [...jobs.values()].filter((j) => j.status !== 'running');
31
+ while (done.length > historyLimit) {
32
+ const oldest = done.shift();
33
+ if (oldest)
34
+ jobs.delete(oldest.id);
35
+ }
36
+ }
37
+ return {
38
+ submit(options, target) {
39
+ if (running)
40
+ return { conflict: true };
41
+ running = true;
42
+ const job = {
43
+ id: randomBytes(16).toString('hex'),
44
+ status: 'running',
45
+ options,
46
+ createdAt: new Date().toISOString(),
47
+ };
48
+ jobs.set(job.id, job);
49
+ // Deferred past the HTTP response flush; the sync engine still blocks
50
+ // the loop while it runs (documented localhost limitation, ADR-0039).
51
+ // Awaited so slow (test) runners hold the single-flight lock across
52
+ // ticks — that is what makes the 409 path deterministic.
53
+ setImmediate(() => {
54
+ void (async () => {
55
+ try {
56
+ const { report, profile } = await runner(options, target);
57
+ job.report = report;
58
+ job.profile = profile;
59
+ job.status = 'done';
60
+ }
61
+ catch (err) {
62
+ job.status = 'error';
63
+ job.error = err.message;
64
+ }
65
+ finally {
66
+ job.finishedAt = new Date().toISOString();
67
+ running = false;
68
+ prune();
69
+ hooks.onDone?.(job);
70
+ }
71
+ })();
72
+ });
73
+ return { id: job.id };
74
+ },
75
+ get(id) {
76
+ return jobs.get(id);
77
+ },
78
+ list() {
79
+ return [...jobs.values()].sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1));
80
+ },
81
+ restore(seed) {
82
+ for (const job of seed) {
83
+ if (job.status !== 'running')
84
+ jobs.set(job.id, job);
85
+ }
86
+ prune();
87
+ },
88
+ };
89
+ }
90
+ export function persistJob(dir, job) {
91
+ if (job.status === 'running')
92
+ return;
93
+ const record = {
94
+ id: job.id,
95
+ status: job.status,
96
+ options: job.options,
97
+ createdAt: job.createdAt,
98
+ finishedAt: job.finishedAt,
99
+ error: job.error,
100
+ rendered: job.rendered,
101
+ };
102
+ fs.writeFileSync(path.join(dir, `${job.id}.json`), `${JSON.stringify(record, null, 2)}\n`);
103
+ }
104
+ function readRecord(dir, file) {
105
+ const raw = JSON.parse(fs.readFileSync(path.join(dir, file), 'utf8'));
106
+ if (typeof raw.id !== 'string' || (raw.status !== 'done' && raw.status !== 'error')) {
107
+ throw new Error('not a job record');
108
+ }
109
+ return raw;
110
+ }
111
+ function listRecordFiles(dir) {
112
+ try {
113
+ return fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
114
+ }
115
+ catch {
116
+ return [];
117
+ }
118
+ }
119
+ /**
120
+ * Read back persisted jobs, skipping anything malformed with a loud
121
+ * warning instead of refusing to start — a corrupt file must not take
122
+ * down the server, but it must not pass silently either.
123
+ */
124
+ export function loadPersisted(dir) {
125
+ const out = [];
126
+ for (const f of listRecordFiles(dir)) {
127
+ try {
128
+ const raw = readRecord(dir, f);
129
+ out.push({
130
+ id: raw.id,
131
+ status: raw.status,
132
+ options: raw.options,
133
+ createdAt: raw.createdAt,
134
+ finishedAt: raw.finishedAt,
135
+ error: raw.error,
136
+ rendered: raw.rendered,
137
+ });
138
+ }
139
+ catch (err) {
140
+ console.error(`serve: skipping unreadable job file ${f}: ${err.message}`);
141
+ }
142
+ }
143
+ return out;
144
+ }
145
+ /** Prune persisted files past the bound, oldest first (shared rule). */
146
+ export function prunePersisted(dir, historyLimit) {
147
+ const records = [];
148
+ for (const f of listRecordFiles(dir)) {
149
+ try {
150
+ const raw = readRecord(dir, f);
151
+ records.push({ id: raw.id, createdAt: raw.createdAt });
152
+ }
153
+ catch {
154
+ // Malformed files are the loader's complaint, not the pruner's.
155
+ }
156
+ }
157
+ if (records.length <= historyLimit)
158
+ return;
159
+ records.sort((a, b) => (a.createdAt < b.createdAt ? -1 : 1));
160
+ for (const victim of records.slice(0, records.length - historyLimit)) {
161
+ try {
162
+ fs.rmSync(path.join(dir, `${victim.id}.json`));
163
+ }
164
+ catch {
165
+ // Best effort: a leftover file is untidy, not a failure.
166
+ }
167
+ }
168
+ }
169
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/serve/jobs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type IncomingMessage, type Server } from 'node:http';
2
+ import { createJobManager } from './jobs.js';
3
+ /** Constant-time bearer check. Length mismatch fails without throwing. */
4
+ export declare function checkAuth(req: IncomingMessage, token: string): boolean;
5
+ export interface ServeOptions {
6
+ port: number;
7
+ token: string;
8
+ roots: string[];
9
+ /** Injected for tests; production runs the real audit engine. */
10
+ runner?: Parameters<typeof createJobManager>[0];
11
+ /** Persist finished jobs here (ADR-0040); unset means memory only. */
12
+ dataDir?: string;
13
+ historyLimit?: number;
14
+ }
15
+ export declare function startServer(opts: ServeOptions): Promise<{
16
+ server: Server;
17
+ port: number;
18
+ token: string;
19
+ }>;
20
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/serve/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAUjG,OAAO,EACL,gBAAgB,EAMjB,MAAM,WAAW,CAAC;AAsBnB,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAQtE;AAkCD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAiED"}
@@ -0,0 +1,275 @@
1
+ import { createServer } from 'node:http';
2
+ import { timingSafeEqual } from 'node:crypto';
3
+ import { runAudit } from '../engine/audit.js';
4
+ import { loadConfig } from '../config.js';
5
+ import { renderJson } from '../report/json.js';
6
+ import { renderMarkdown } from '../report/markdown.js';
7
+ import { renderHtml } from '../report/html.js';
8
+ import { DEPTHS, MATURITIES, DEFAULT_RULES_DIR, VERSION } from '../cli-args.js';
9
+ import fs from 'node:fs';
10
+ import path from 'node:path';
11
+ import { createJobManager, loadPersisted, persistJob, prunePersisted, resolveTarget, } from './jobs.js';
12
+ import { uiPage } from './ui.js';
13
+ /**
14
+ * `usa serve` HTTP layer (ADR-0039): loopback only, bearer-token auth on
15
+ * every API route, targets contained in allow-roots, one audit at a time.
16
+ *
17
+ * CSRF note: browsers can only reach this server cross-origin with simple
18
+ * requests, which cannot carry the Authorization header — and no CORS
19
+ * headers are ever sent, so preflighted reads fail closed. There are no
20
+ * cookies and no ambient authority; the token is the whole credential.
21
+ */
22
+ const BODY_LIMIT_BYTES = 64 * 1024;
23
+ const FORMATS = ['json', 'html', 'md'];
24
+ function sendJson(res, code, obj) {
25
+ const body = `${JSON.stringify(obj)}\n`;
26
+ res.writeHead(code, { 'content-type': 'application/json; charset=utf-8' });
27
+ res.end(body);
28
+ }
29
+ /** Constant-time bearer check. Length mismatch fails without throwing. */
30
+ export function checkAuth(req, token) {
31
+ const header = req.headers.authorization;
32
+ if (typeof header !== 'string')
33
+ return false;
34
+ const match = /^Bearer (.+)$/.exec(header);
35
+ if (!match)
36
+ return false;
37
+ const presented = Buffer.from(match[1] ?? '');
38
+ const expected = Buffer.from(token);
39
+ return presented.length === expected.length && timingSafeEqual(presented, expected);
40
+ }
41
+ function readBody(req) {
42
+ return new Promise((resolve, reject) => {
43
+ const chunks = [];
44
+ let total = 0;
45
+ req.on('data', (c) => {
46
+ total += c.length;
47
+ if (total > BODY_LIMIT_BYTES) {
48
+ reject(new Error('request body too large'));
49
+ return;
50
+ }
51
+ chunks.push(c);
52
+ });
53
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
54
+ req.on('error', reject);
55
+ });
56
+ }
57
+ function renderJobReport(job) {
58
+ const format = job.options.format;
59
+ if (!job.report || !job.profile)
60
+ return { contentType: 'application/json', body: '{}' };
61
+ if (format === 'html') {
62
+ return { contentType: 'text/html; charset=utf-8', body: renderHtml(job.report, job.profile) };
63
+ }
64
+ if (format === 'md') {
65
+ return {
66
+ contentType: 'text/markdown; charset=utf-8',
67
+ body: renderMarkdown(job.report, job.profile),
68
+ };
69
+ }
70
+ return { contentType: 'application/json; charset=utf-8', body: renderJson(job.report) };
71
+ }
72
+ export async function startServer(opts) {
73
+ const historyLimit = opts.historyLimit ?? 20;
74
+ const dataDir = opts.dataDir === undefined ? undefined : path.resolve(opts.dataDir);
75
+ if (dataDir !== undefined) {
76
+ try {
77
+ fs.mkdirSync(dataDir, { recursive: true });
78
+ }
79
+ catch (err) {
80
+ throw new Error(`serve: cannot create data dir ${dataDir}: ${err.message}`, {
81
+ cause: err,
82
+ });
83
+ }
84
+ }
85
+ const hooks = dataDir === undefined
86
+ ? {}
87
+ : {
88
+ onDone: (job) => {
89
+ if (job.status === 'done' && job.report && job.profile) {
90
+ job.rendered = renderJobReport(job);
91
+ }
92
+ persistJob(dataDir, job);
93
+ prunePersisted(dataDir, historyLimit);
94
+ },
95
+ };
96
+ const manager = createJobManager(opts.runner ??
97
+ ((jobOptions, target) => {
98
+ const config = loadConfig(target);
99
+ const { report, profile } = runAudit({
100
+ target,
101
+ rulesDir: DEFAULT_RULES_DIR,
102
+ depth: jobOptions.depth,
103
+ profile: jobOptions.profile,
104
+ config,
105
+ allowCommands: false,
106
+ usaVersion: VERSION,
107
+ });
108
+ return { report, profile };
109
+ }), historyLimit, hooks);
110
+ if (dataDir !== undefined)
111
+ manager.restore(loadPersisted(dataDir));
112
+ const ctx = {
113
+ manager,
114
+ token: opts.token,
115
+ roots: opts.roots,
116
+ };
117
+ const server = createServer((req, res) => {
118
+ handleRequest(req, res, ctx).catch((err) => {
119
+ if (!res.headersSent)
120
+ sendJson(res, 500, { error: 'internal error' });
121
+ console.error(`serve: request failed: ${err.message}`);
122
+ });
123
+ });
124
+ await new Promise((resolve, reject) => {
125
+ server.once('error', reject);
126
+ // Loopback only — ADR-0039: remote exposure is a reverse proxy's job.
127
+ // There is no flag to change this; serving remotely needs new code.
128
+ server.listen(opts.port, '127.0.0.1', () => resolve());
129
+ });
130
+ const address = server.address();
131
+ const port = typeof address === 'object' && address !== null ? address.port : opts.port;
132
+ return { server, port, token: opts.token };
133
+ }
134
+ function depthError(depth) {
135
+ if (typeof depth !== 'string' || !DEPTHS.includes(depth)) {
136
+ return `depth must be one of ${DEPTHS.join('|')}`;
137
+ }
138
+ return null;
139
+ }
140
+ function profileError(profile) {
141
+ if (typeof profile !== 'string' ||
142
+ (profile !== 'auto' && !MATURITIES.includes(profile))) {
143
+ return 'profile must be auto or a maturity stage';
144
+ }
145
+ return null;
146
+ }
147
+ function formatError(format) {
148
+ if (typeof format !== 'string' || !FORMATS.includes(format)) {
149
+ return `format must be one of ${FORMATS.join('|')}`;
150
+ }
151
+ return null;
152
+ }
153
+ function validateAuditBody(body) {
154
+ const target = body['target'];
155
+ if (typeof target !== 'string' || target.length === 0) {
156
+ return { error: 'target (string) is required' };
157
+ }
158
+ const depth = body['depth'] ?? 'standard';
159
+ const profile = body['profile'] ?? 'auto';
160
+ const format = body['format'] ?? 'json';
161
+ for (const err of [depthError(depth), profileError(profile), formatError(format)]) {
162
+ if (err)
163
+ return { error: err };
164
+ }
165
+ return {
166
+ options: {
167
+ target,
168
+ depth: depth,
169
+ profile: profile,
170
+ format: format,
171
+ },
172
+ };
173
+ }
174
+ async function handlePostAudits(req, res, ctx) {
175
+ let raw;
176
+ try {
177
+ raw = await readBody(req);
178
+ }
179
+ catch {
180
+ sendJson(res, 413, { error: 'request body too large' });
181
+ return;
182
+ }
183
+ let body;
184
+ try {
185
+ body = JSON.parse(raw);
186
+ }
187
+ catch {
188
+ sendJson(res, 400, { error: 'request body must be JSON' });
189
+ return;
190
+ }
191
+ const valid = validateAuditBody(body);
192
+ if ('error' in valid) {
193
+ sendJson(res, 400, { error: valid.error });
194
+ return;
195
+ }
196
+ let resolved;
197
+ try {
198
+ resolved = resolveTarget(valid.options.target, ctx.roots);
199
+ }
200
+ catch (err) {
201
+ const message = err.message;
202
+ // Containment failures are authorization failures; a missing
203
+ // path is a bad request. Different status, same static shape.
204
+ sendJson(res, message.includes('escapes') ? 403 : 400, { error: message });
205
+ return;
206
+ }
207
+ const submitted = ctx.manager.submit({ ...valid.options }, resolved);
208
+ if ('conflict' in submitted) {
209
+ sendJson(res, 409, { error: 'an audit is already running' });
210
+ return;
211
+ }
212
+ sendJson(res, 202, { id: submitted.id, status: 'running' });
213
+ }
214
+ function jobSummary(j) {
215
+ return {
216
+ id: j.id,
217
+ status: j.status,
218
+ createdAt: j.createdAt,
219
+ ...(j.finishedAt === undefined ? {} : { finishedAt: j.finishedAt }),
220
+ options: j.options,
221
+ ...(j.error === undefined ? {} : { error: j.error }),
222
+ };
223
+ }
224
+ function handleListAudits(res, ctx) {
225
+ sendJson(res, 200, { audits: ctx.manager.list().map(jobSummary) });
226
+ }
227
+ function handleGetAudit(res, ctx, id) {
228
+ const job = ctx.manager.get(id);
229
+ if (!job) {
230
+ sendJson(res, 404, { error: 'unknown audit id' });
231
+ return;
232
+ }
233
+ // Restored jobs carry only the filed rendering; live jobs render.
234
+ const rendered = job.rendered ?? (job.status === 'done' ? renderJobReport(job) : undefined);
235
+ sendJson(res, 200, {
236
+ ...jobSummary(job),
237
+ ...(rendered === undefined ? {} : { contentType: rendered.contentType, report: rendered.body }),
238
+ });
239
+ }
240
+ /** Request URL against the loopback base (the server never sees another host). */
241
+ function requestUrl(req) {
242
+ return new URL(req.url ?? '/', 'http://127.0.0.1');
243
+ }
244
+ async function handleRequest(req, res, ctx) {
245
+ const url = requestUrl(req);
246
+ if (req.method === 'GET' && url.pathname === '/') {
247
+ res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
248
+ res.end(uiPage());
249
+ return;
250
+ }
251
+ if (!checkAuth(req, ctx.token)) {
252
+ sendJson(res, 401, { error: 'missing or invalid bearer token' });
253
+ return;
254
+ }
255
+ if (req.method === 'POST' && url.pathname === '/api/audits') {
256
+ await handlePostAudits(req, res, ctx);
257
+ return;
258
+ }
259
+ if (req.method === 'GET' && url.pathname === '/api/audits') {
260
+ handleListAudits(res, ctx);
261
+ return;
262
+ }
263
+ const auditId = matchAuditId(url.pathname);
264
+ if (req.method === 'GET' && auditId) {
265
+ handleGetAudit(res, ctx, auditId);
266
+ return;
267
+ }
268
+ sendJson(res, 404, { error: 'not found' });
269
+ }
270
+ /** Extract a hex audit id from /api/audits/:id, or null. */
271
+ function matchAuditId(pathname) {
272
+ const match = /^\/api\/audits\/([0-9a-f]+)$/.exec(pathname);
273
+ return match?.[1] ?? null;
274
+ }
275
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/serve/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The `usa serve` landing page: a form that drives the JSON API and renders
3
+ * results inline. Everything dynamic is built with textContent and
4
+ * createElement — the string `innerHTML` must never appear in this file
5
+ * (report content derives from audited trees and is untrusted). There is a
6
+ * test asserting that.
7
+ */
8
+ export declare function uiPage(): string;
9
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/serve/ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,MAAM,IAAI,MAAM,CA4H/B"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * The `usa serve` landing page: a form that drives the JSON API and renders
3
+ * results inline. Everything dynamic is built with textContent and
4
+ * createElement — the string `innerHTML` must never appear in this file
5
+ * (report content derives from audited trees and is untrusted). There is a
6
+ * test asserting that.
7
+ */
8
+ export function uiPage() {
9
+ return `<!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1">
14
+ <title>USA — local audit server</title>
15
+ <style>
16
+ body { font-family: system-ui, sans-serif; max-width: 60rem; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; }
17
+ label { display: block; margin: .5rem 0; }
18
+ input, select { font: inherit; padding: .2rem .4rem; }
19
+ #status { margin: 1rem 0; font-weight: bold; }
20
+ .finding { border: 1px solid currentColor; border-radius: .5rem; padding: .5rem 1rem; margin: .5rem 0; }
21
+ pre { white-space: pre-wrap; word-break: break-word; background: rgba(127,127,127,.12); padding: 1rem; }
22
+ </style>
23
+ </head>
24
+ <body>
25
+ <h1>USA — local audit server</h1>
26
+ <p>Loopback only. Paste the bearer token printed at startup; it authenticates every API call.</p>
27
+ <form id="run">
28
+ <label>Token <input id="token" type="password" autocomplete="off"></label>
29
+ <label>Target <input id="target" value="."></label>
30
+ <label>Depth <select id="depth"><option>quick</option><option selected>standard</option><option>deep</option></select></label>
31
+ <label>Format <select id="format"><option selected>json</option><option>html</option><option>md</option></select></label>
32
+ <button type="submit">Run audit</button>
33
+ </form>
34
+ <div id="status"></div>
35
+ <div id="result"></div>
36
+ <h2>History</h2>
37
+ <button id="refresh" type="button">Refresh</button>
38
+ <div id="history"></div>
39
+ <script>
40
+ (function () {
41
+ var form = document.getElementById('run');
42
+ var status = document.getElementById('status');
43
+ var result = document.getElementById('result');
44
+ function say(text) { status.textContent = text; }
45
+ function clear(node) { while (node.firstChild) node.removeChild(node.firstChild); }
46
+ function line(parent, tag, text) {
47
+ var el = document.createElement(tag);
48
+ el.textContent = text;
49
+ parent.appendChild(el);
50
+ return el;
51
+ }
52
+ function api(path, options) {
53
+ var token = document.getElementById('token').value;
54
+ options = options || {};
55
+ options.headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };
56
+ return fetch(path, options).then(function (r) {
57
+ return r.json().then(function (body) { return { code: r.status, body: body }; });
58
+ });
59
+ }
60
+ function loadHistory() {
61
+ var history = document.getElementById('history');
62
+ clear(history);
63
+ api('/api/audits').then(function (got) {
64
+ if (got.code !== 200) { line(history, 'p', 'History: ' + (got.body.error || got.code)); return; }
65
+ (got.body.audits || []).forEach(function (a) {
66
+ var row = line(history, 'div', '');
67
+ var link = document.createElement('a');
68
+ link.href = '#';
69
+ link.textContent = a.id.slice(0, 8) + ' — ' + a.status + ' — ' + a.options.target;
70
+ link.addEventListener('click', function (ev) {
71
+ ev.preventDefault();
72
+ api('/api/audits/' + a.id).then(function (one) {
73
+ if (one.code !== 200) { say('Error: ' + (one.body.error || one.code)); return; }
74
+ say(one.body.status === 'done' ? 'Done.' : one.body.status + '.');
75
+ if (one.body.status === 'done') render(one.body);
76
+ }).catch(function (err) { say('Request failed: ' + err); });
77
+ });
78
+ row.appendChild(link);
79
+ });
80
+ }).catch(function (err) { line(history, 'p', 'History failed: ' + err); });
81
+ }
82
+ document.getElementById('refresh').addEventListener('click', loadHistory);
83
+ form.addEventListener('submit', function (ev) {
84
+ ev.preventDefault();
85
+ clear(result);
86
+ var payload = {
87
+ target: document.getElementById('target').value,
88
+ depth: document.getElementById('depth').value,
89
+ format: document.getElementById('format').value,
90
+ };
91
+ say('Starting…');
92
+ api('/api/audits', { method: 'POST', body: JSON.stringify(payload) }).then(function (started) {
93
+ if (started.code !== 202) { say('Error: ' + (started.body.error || started.code)); return; }
94
+ say('Running…');
95
+ var timer = setInterval(function () {
96
+ api('/api/audits/' + started.body.id).then(function (got) {
97
+ if (got.code !== 200) { clearInterval(timer); say('Error: ' + (got.body.error || got.code)); return; }
98
+ if (got.body.status === 'running') return;
99
+ clearInterval(timer);
100
+ if (got.body.status === 'error') { say('Audit failed: ' + got.body.error); return; }
101
+ say('Done.');
102
+ render(got.body);
103
+ loadHistory();
104
+ }).catch(function (err) { clearInterval(timer); say('Request failed: ' + err); });
105
+ }, 1000);
106
+ }).catch(function (err) { say('Request failed: ' + err); });
107
+ });
108
+ function render(job) {
109
+ clear(result);
110
+ if (job.options.format !== 'json') {
111
+ line(result, 'pre', job.report);
112
+ return;
113
+ }
114
+ var doc;
115
+ try { doc = JSON.parse(job.report); }
116
+ catch (err) { line(result, 'pre', job.report); return; }
117
+ var findings = doc.findings || [];
118
+ var open = findings.filter(function (f) { return f.status !== 'PASS' && f.status !== 'UNKNOWN'; });
119
+ line(result, 'h2', 'Score ' + doc.score.overall + '/100 — ' + open.length + ' open findings');
120
+ open.forEach(function (f) {
121
+ var box = line(result, 'div', '');
122
+ box.className = 'finding';
123
+ line(box, 'h3', f.ruleId + ' — ' + f.title);
124
+ line(box, 'p', f.message);
125
+ });
126
+ }
127
+ })();
128
+ </script>
129
+ </body>
130
+ </html>
131
+ `;
132
+ }
133
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../../src/serve/ui.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,MAAM;IACpB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0HR,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenos1996/usa",
3
- "version": "2.16.1",
3
+ "version": "2.18.0",
4
4
  "description": "Universal Software Auditor — an open-source, self-adapting audit framework for any project, any stack, any stage",
5
5
  "license": "MIT",
6
6
  "type": "module",