@xenos1996/usa 2.16.0 → 2.17.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,QA0GT,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,11 @@ 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
+
84
90
  audit options
85
91
  --dry-run Print the report path that would be written and exit
86
92
  --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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GnB,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,8 @@
1
+ import { type Args } from './cli-args.js';
2
+ /**
3
+ * `usa serve`: loopback-only audit server (ADR-0039). Binds 127.0.0.1 —
4
+ * there is deliberately no flag to change that. Targets must exist and
5
+ * resolve inside --allow-root (default: the working directory).
6
+ */
7
+ export declare function cmdServe(args: Args): Promise<number>;
8
+ //# 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;AAErD;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAkC1D"}
@@ -0,0 +1,46 @@
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
+ /**
7
+ * `usa serve`: loopback-only audit server (ADR-0039). Binds 127.0.0.1 —
8
+ * there is deliberately no flag to change that. Targets must exist and
9
+ * resolve inside --allow-root (default: the working directory).
10
+ */
11
+ export async function cmdServe(args) {
12
+ const portRaw = str(args, 'port', '0') ?? '0';
13
+ const port = Number(portRaw);
14
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
15
+ console.error('--port must be an integer 0-65535 (0 picks an ephemeral port)');
16
+ return 2;
17
+ }
18
+ const roots = list(args, 'allow-root');
19
+ const resolvedRoots = (roots.length > 0 ? roots : [process.cwd()]).map((r) => path.resolve(r));
20
+ for (const r of resolvedRoots) {
21
+ if (!fs.existsSync(r)) {
22
+ console.error(`--allow-root does not exist: ${r}`);
23
+ return 2;
24
+ }
25
+ }
26
+ const given = str(args, 'token');
27
+ const token = given ?? randomBytes(32).toString('hex');
28
+ try {
29
+ const { port: bound } = await startServer({ port, token, roots: resolvedRoots });
30
+ console.log(`usa serve: listening on http://127.0.0.1:${bound} (loopback only — never expose directly)`);
31
+ if (given === undefined) {
32
+ console.log('usa serve: generated bearer token (printed once, required on /api/*):');
33
+ console.log(token);
34
+ }
35
+ else {
36
+ console.log('usa serve: using the provided bearer token for /api/*.');
37
+ }
38
+ }
39
+ catch (err) {
40
+ console.error(`usa serve: cannot listen: ${err.message}`);
41
+ return 2;
42
+ }
43
+ await new Promise(() => { });
44
+ return 0;
45
+ }
46
+ //# sourceMappingURL=cli-serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-serve.js","sourceRoot":"","sources":["../src/cli-serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAa,MAAM,eAAe,CAAC;AAErD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAU;IACvC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC;IAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;QAC/E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IACvC,MAAM,aAAa,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,KAAK,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QACjF,OAAO,CAAC,GAAG,CACT,4CAA4C,KAAK,0CAA0C,CAC5F,CAAC;QACF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;YACrF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,6BAA8B,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,OAAO,CAAO,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClC,OAAO,CAAC,CAAC;AACX,CAAC"}
package/dist/cli.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  export declare function main(argv: string[]): number | Promise<number>;
2
3
  /** Resolves the report format from an explicit flag, else the --out suffix. */
3
4
  declare function isEntryPoint(argv1?: string | undefined, thisUrl?: string): boolean;
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAiDA,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
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  import fs from 'node:fs';
2
3
  import { pathToFileURL } from 'node:url';
3
4
  import { VERSION, bool, parseArgs } from './cli-args.js';
@@ -9,6 +10,7 @@ import { cmdFoundation, cmdLive, cmdModels } from './cli-session.js';
9
10
  import { cmdDiff, cmdVerifyReport } from './cli-reports.js';
10
11
  import { cmdBootstrap, cmdInit, cmdLearn } from './cli-scaffold.js';
11
12
  import { cmdEvolve } from './cli-evolve.js';
13
+ import { cmdServe } from './cli-serve.js';
12
14
  /** Dispatch only: every command lives in its own cli-*.ts module. */
13
15
  /* -------------------------------------------------------------------- main -- */
14
16
  const COMMANDS = {
@@ -27,6 +29,7 @@ const COMMANDS = {
27
29
  categories: cmdCategories,
28
30
  live: cmdLive,
29
31
  models: cmdModels,
32
+ serve: cmdServe,
30
33
  };
31
34
  /** Global --help/--version, resolved before dispatch. Returns null to continue. */
32
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,52 @@
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
+ error?: string;
28
+ }
29
+ /**
30
+ * Resolve a requested target against allow-roots. Throws a clean Error when
31
+ * the target does not exist or escapes every root (after realpath, so
32
+ * symlinks cannot smuggle it out).
33
+ */
34
+ export declare function resolveTarget(raw: string, roots: string[]): string;
35
+ export interface JobRunner {
36
+ (options: ServeJobOptions, target: string): {
37
+ report: AuditReport;
38
+ profile: MaturityProfile;
39
+ } | Promise<{
40
+ report: AuditReport;
41
+ profile: MaturityProfile;
42
+ }>;
43
+ }
44
+ export declare function createJobManager(runner: JobRunner, historyLimit?: number): {
45
+ submit: (options: ServeJobOptions, target: string) => {
46
+ id: string;
47
+ } | {
48
+ conflict: true;
49
+ };
50
+ get: (id: string) => ServeJob | undefined;
51
+ };
52
+ //# 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,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,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,EACjB,YAAY,SAAK,GAChB;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;CAC3C,CAmDA"}
@@ -0,0 +1,79 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { 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) {
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
+ }
70
+ })();
71
+ });
72
+ return { id: job.id };
73
+ },
74
+ get(id) {
75
+ return jobs.get(id);
76
+ },
77
+ };
78
+ }
79
+ //# 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,16 @@
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
+ manager?: ReturnType<typeof createJobManager>;
10
+ }
11
+ export declare function startServer(opts: ServeOptions): Promise<{
12
+ server: Server;
13
+ port: number;
14
+ token: string;
15
+ }>;
16
+ //# 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;AAQjG,OAAO,EAAE,gBAAgB,EAAgC,MAAM,WAAW,CAAC;AAsB3E,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,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;CAC/C;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,CA0HD"}
@@ -0,0 +1,192 @@
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 { createJobManager, resolveTarget } from './jobs.js';
10
+ import { uiPage } from './ui.js';
11
+ /**
12
+ * `usa serve` HTTP layer (ADR-0039): loopback only, bearer-token auth on
13
+ * every API route, targets contained in allow-roots, one audit at a time.
14
+ *
15
+ * CSRF note: browsers can only reach this server cross-origin with simple
16
+ * requests, which cannot carry the Authorization header — and no CORS
17
+ * headers are ever sent, so preflighted reads fail closed. There are no
18
+ * cookies and no ambient authority; the token is the whole credential.
19
+ */
20
+ const BODY_LIMIT_BYTES = 64 * 1024;
21
+ const FORMATS = ['json', 'html', 'md'];
22
+ function sendJson(res, code, obj) {
23
+ const body = `${JSON.stringify(obj)}\n`;
24
+ res.writeHead(code, { 'content-type': 'application/json; charset=utf-8' });
25
+ res.end(body);
26
+ }
27
+ /** Constant-time bearer check. Length mismatch fails without throwing. */
28
+ export function checkAuth(req, token) {
29
+ const header = req.headers.authorization;
30
+ if (typeof header !== 'string')
31
+ return false;
32
+ const match = /^Bearer (.+)$/.exec(header);
33
+ if (!match)
34
+ return false;
35
+ const presented = Buffer.from(match[1] ?? '');
36
+ const expected = Buffer.from(token);
37
+ return presented.length === expected.length && timingSafeEqual(presented, expected);
38
+ }
39
+ function readBody(req) {
40
+ return new Promise((resolve, reject) => {
41
+ const chunks = [];
42
+ let total = 0;
43
+ req.on('data', (c) => {
44
+ total += c.length;
45
+ if (total > BODY_LIMIT_BYTES) {
46
+ reject(new Error('request body too large'));
47
+ return;
48
+ }
49
+ chunks.push(c);
50
+ });
51
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
52
+ req.on('error', reject);
53
+ });
54
+ }
55
+ function renderJobReport(job) {
56
+ const format = job.options.format;
57
+ if (!job.report || !job.profile)
58
+ return { contentType: 'application/json', body: '{}' };
59
+ if (format === 'html') {
60
+ return { contentType: 'text/html; charset=utf-8', body: renderHtml(job.report, job.profile) };
61
+ }
62
+ if (format === 'md') {
63
+ return {
64
+ contentType: 'text/markdown; charset=utf-8',
65
+ body: renderMarkdown(job.report, job.profile),
66
+ };
67
+ }
68
+ return { contentType: 'application/json; charset=utf-8', body: renderJson(job.report) };
69
+ }
70
+ export async function startServer(opts) {
71
+ const manager = opts.manager ??
72
+ createJobManager((jobOptions, target) => {
73
+ const config = loadConfig(target);
74
+ const { report, profile } = runAudit({
75
+ target,
76
+ rulesDir: DEFAULT_RULES_DIR,
77
+ depth: jobOptions.depth,
78
+ profile: jobOptions.profile,
79
+ config,
80
+ allowCommands: false,
81
+ usaVersion: VERSION,
82
+ });
83
+ return { report, profile };
84
+ });
85
+ const server = createServer((req, res) => {
86
+ void (async () => {
87
+ const url = new URL(req.url ?? '/', 'http://127.0.0.1');
88
+ if (req.method === 'GET' && url.pathname === '/') {
89
+ res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
90
+ res.end(uiPage());
91
+ return;
92
+ }
93
+ if (!checkAuth(req, opts.token)) {
94
+ sendJson(res, 401, { error: 'missing or invalid bearer token' });
95
+ return;
96
+ }
97
+ if (req.method === 'POST' && url.pathname === '/api/audits') {
98
+ let raw;
99
+ try {
100
+ raw = await readBody(req);
101
+ }
102
+ catch {
103
+ sendJson(res, 413, { error: 'request body too large' });
104
+ return;
105
+ }
106
+ let body;
107
+ try {
108
+ body = JSON.parse(raw);
109
+ }
110
+ catch {
111
+ sendJson(res, 400, { error: 'request body must be JSON' });
112
+ return;
113
+ }
114
+ const target = body['target'];
115
+ const depth = body['depth'] ?? 'standard';
116
+ const profile = body['profile'] ?? 'auto';
117
+ const format = body['format'] ?? 'json';
118
+ if (typeof target !== 'string' || target.length === 0) {
119
+ sendJson(res, 400, { error: 'target (string) is required' });
120
+ return;
121
+ }
122
+ if (typeof depth !== 'string' || !DEPTHS.includes(depth)) {
123
+ sendJson(res, 400, { error: `depth must be one of ${DEPTHS.join('|')}` });
124
+ return;
125
+ }
126
+ if (typeof profile !== 'string' ||
127
+ (profile !== 'auto' && !MATURITIES.includes(profile))) {
128
+ sendJson(res, 400, { error: 'profile must be auto or a maturity stage' });
129
+ return;
130
+ }
131
+ if (typeof format !== 'string' || !FORMATS.includes(format)) {
132
+ sendJson(res, 400, { error: `format must be one of ${FORMATS.join('|')}` });
133
+ return;
134
+ }
135
+ let resolved;
136
+ try {
137
+ resolved = resolveTarget(target, opts.roots);
138
+ }
139
+ catch (err) {
140
+ const message = err.message;
141
+ // Containment failures are authorization failures; a missing
142
+ // path is a bad request. Different status, same static shape.
143
+ sendJson(res, message.includes('escapes') ? 403 : 400, { error: message });
144
+ return;
145
+ }
146
+ const submitted = manager.submit({ target, depth, profile, format }, resolved);
147
+ if ('conflict' in submitted) {
148
+ sendJson(res, 409, { error: 'an audit is already running' });
149
+ return;
150
+ }
151
+ sendJson(res, 202, { id: submitted.id, status: 'running' });
152
+ return;
153
+ }
154
+ const auditMatch = /^\/api\/audits\/([0-9a-f]+)$/.exec(url.pathname);
155
+ if (req.method === 'GET' && auditMatch) {
156
+ const job = manager.get(auditMatch[1]);
157
+ if (!job) {
158
+ sendJson(res, 404, { error: 'unknown audit id' });
159
+ return;
160
+ }
161
+ const rendered = job.status === 'done' ? renderJobReport(job) : undefined;
162
+ sendJson(res, 200, {
163
+ id: job.id,
164
+ status: job.status,
165
+ createdAt: job.createdAt,
166
+ ...(job.finishedAt === undefined ? {} : { finishedAt: job.finishedAt }),
167
+ options: job.options,
168
+ ...(job.error === undefined ? {} : { error: job.error }),
169
+ ...(rendered === undefined
170
+ ? {}
171
+ : { contentType: rendered.contentType, report: rendered.body }),
172
+ });
173
+ return;
174
+ }
175
+ sendJson(res, 404, { error: 'not found' });
176
+ })().catch((err) => {
177
+ if (!res.headersSent)
178
+ sendJson(res, 500, { error: 'internal error' });
179
+ console.error(`serve: request failed: ${err.message}`);
180
+ });
181
+ });
182
+ await new Promise((resolve, reject) => {
183
+ server.once('error', reject);
184
+ // Loopback only — ADR-0039: remote exposure is a reverse proxy's job.
185
+ // There is no flag to change this; serving remotely needs new code.
186
+ server.listen(opts.port, '127.0.0.1', () => resolve());
187
+ });
188
+ const address = server.address();
189
+ const port = typeof address === 'object' && address !== null ? address.port : opts.port;
190
+ return { server, port, token: opts.token };
191
+ }
192
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/serve/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA0D,MAAM,WAAW,CAAC;AACjG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAiB,MAAM,WAAW,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC;;;;;;;;GAQG;AAEH,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AACnC,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AAEhD,SAAS,QAAQ,CAAC,GAAmB,EAAE,IAAY,EAAE,GAAY;IAC/D,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;IACxC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,iCAAiC,EAAE,CAAC,CAAC;IAC3E,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,GAAoB,EAAE,KAAa;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC7C,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,SAAS,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AACtF,CAAC;AAED,SAAS,QAAQ,CAAC,GAAoB;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YAC3B,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;YAClB,IAAI,KAAK,GAAG,gBAAgB,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;gBAC5C,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACrE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,GAAa;IACpC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxF,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,EAAE,WAAW,EAAE,0BAA0B,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;IAChG,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,WAAW,EAAE,8BAA8B;YAC3C,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;SAC9C,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,iCAAiC,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;AAC1F,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAkB;IAKlD,MAAM,OAAO,GACX,IAAI,CAAC,OAAO;QACZ,gBAAgB,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;gBACnC,MAAM;gBACN,QAAQ,EAAE,iBAAiB;gBAC3B,KAAK,EAAE,UAAU,CAAC,KAAsC;gBACxD,OAAO,EAAE,UAAU,CAAC,OAAiB;gBACrC,MAAM;gBACN,aAAa,EAAE,KAAK;gBACpB,UAAU,EAAE,OAAO;aACpB,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;IAEL,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;YACxD,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;gBACjD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;gBACnE,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;gBAClB,OAAO;YACT,CAAC;YACD,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAC;gBACjE,OAAO;YACT,CAAC;YACD,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;gBAC5D,IAAI,GAAW,CAAC;gBAChB,IAAI,CAAC;oBACH,GAAG,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC;oBACxD,OAAO;gBACT,CAAC;gBACD,IAAI,IAA6B,CAAC;gBAClC,IAAI,CAAC;oBACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;gBACpD,CAAC;gBAAC,MAAM,CAAC;oBACP,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC,CAAC;oBAC3D,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC;gBAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC;gBAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC;gBACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACtD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC,CAAC;oBAC7D,OAAO;gBACT,CAAC;gBACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAgB,CAAC,EAAE,CAAC;oBACpE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,wBAAwB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC1E,OAAO;gBACT,CAAC;gBACD,IACE,OAAO,OAAO,KAAK,QAAQ;oBAC3B,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAiB,CAAC,CAAC,EAC/D,CAAC;oBACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,0CAA0C,EAAE,CAAC,CAAC;oBAC1E,OAAO;gBACT,CAAC;gBACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAE,OAA6B,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnF,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;oBAC5E,OAAO;gBACT,CAAC;gBACD,IAAI,QAAgB,CAAC;gBACrB,IAAI,CAAC;oBACH,QAAQ,GAAG,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/C,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,OAAO,GAAI,GAAa,CAAC,OAAO,CAAC;oBACvC,6DAA6D;oBAC7D,8DAA8D;oBAC9D,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;oBAC3E,OAAO;gBACT,CAAC;gBACD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;gBAC/E,IAAI,UAAU,IAAI,SAAS,EAAE,CAAC;oBAC5B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,6BAA6B,EAAE,CAAC,CAAC;oBAC7D,OAAO;gBACT,CAAC;gBACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC5D,OAAO;YACT,CAAC;YACD,MAAM,UAAU,GAAG,8BAA8B,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACrE,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,UAAU,EAAE,CAAC;gBACvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAW,CAAC,CAAC;gBACjD,IAAI,CAAC,GAAG,EAAE,CAAC;oBACT,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAClD,OAAO;gBACT,CAAC;gBACD,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC1E,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE;oBACjB,EAAE,EAAE,GAAG,CAAC,EAAE;oBACV,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,GAAG,CAAC,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;oBACvE,OAAO,EAAE,GAAG,CAAC,OAAO;oBACpB,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;oBACxD,GAAG,CAAC,QAAQ,KAAK,SAAS;wBACxB,CAAC,CAAC,EAAE;wBACJ,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;iBAClE,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;YACxB,IAAI,CAAC,GAAG,CAAC,WAAW;gBAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACtE,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,sEAAsE;QACtE,oEAAoE;QACpE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IACxF,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;AAC7C,CAAC"}
@@ -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,CAiG/B"}
@@ -0,0 +1,106 @@
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
+ <script>
37
+ (function () {
38
+ var form = document.getElementById('run');
39
+ var status = document.getElementById('status');
40
+ var result = document.getElementById('result');
41
+ function say(text) { status.textContent = text; }
42
+ function clear(node) { while (node.firstChild) node.removeChild(node.firstChild); }
43
+ function line(parent, tag, text) {
44
+ var el = document.createElement(tag);
45
+ el.textContent = text;
46
+ parent.appendChild(el);
47
+ return el;
48
+ }
49
+ function api(path, options) {
50
+ var token = document.getElementById('token').value;
51
+ options = options || {};
52
+ options.headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };
53
+ return fetch(path, options).then(function (r) {
54
+ return r.json().then(function (body) { return { code: r.status, body: body }; });
55
+ });
56
+ }
57
+ form.addEventListener('submit', function (ev) {
58
+ ev.preventDefault();
59
+ clear(result);
60
+ var payload = {
61
+ target: document.getElementById('target').value,
62
+ depth: document.getElementById('depth').value,
63
+ format: document.getElementById('format').value,
64
+ };
65
+ say('Starting…');
66
+ api('/api/audits', { method: 'POST', body: JSON.stringify(payload) }).then(function (started) {
67
+ if (started.code !== 202) { say('Error: ' + (started.body.error || started.code)); return; }
68
+ say('Running…');
69
+ var timer = setInterval(function () {
70
+ api('/api/audits/' + started.body.id).then(function (got) {
71
+ if (got.code !== 200) { clearInterval(timer); say('Error: ' + (got.body.error || got.code)); return; }
72
+ if (got.body.status === 'running') return;
73
+ clearInterval(timer);
74
+ if (got.body.status === 'error') { say('Audit failed: ' + got.body.error); return; }
75
+ say('Done.');
76
+ render(got.body);
77
+ }).catch(function (err) { clearInterval(timer); say('Request failed: ' + err); });
78
+ }, 1000);
79
+ }).catch(function (err) { say('Request failed: ' + err); });
80
+ });
81
+ function render(job) {
82
+ clear(result);
83
+ if (job.options.format !== 'json') {
84
+ line(result, 'pre', job.report);
85
+ return;
86
+ }
87
+ var doc;
88
+ try { doc = JSON.parse(job.report); }
89
+ catch (err) { line(result, 'pre', job.report); return; }
90
+ var findings = doc.findings || [];
91
+ var open = findings.filter(function (f) { return f.status !== 'PASS' && f.status !== 'UNKNOWN'; });
92
+ line(result, 'h2', 'Score ' + doc.score.overall + '/100 — ' + open.length + ' open findings');
93
+ open.forEach(function (f) {
94
+ var box = line(result, 'div', '');
95
+ box.className = 'finding';
96
+ line(box, 'h3', f.ruleId + ' — ' + f.title);
97
+ line(box, 'p', f.message);
98
+ });
99
+ }
100
+ })();
101
+ </script>
102
+ </body>
103
+ </html>
104
+ `;
105
+ }
106
+ //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+FR,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenos1996/usa",
3
- "version": "2.16.0",
3
+ "version": "2.17.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",