@cogenta/auth 0.3.0 → 0.5.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/dist/api-keys.d.ts +93 -4
- package/dist/api-keys.d.ts.map +1 -1
- package/dist/api-keys.js +260 -27
- package/dist/api-keys.js.map +1 -1
- package/dist/audit-integrity.d.ts +61 -0
- package/dist/audit-integrity.d.ts.map +1 -0
- package/dist/audit-integrity.js +130 -0
- package/dist/audit-integrity.js.map +1 -0
- package/dist/audit.d.ts +78 -2
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +168 -24
- package/dist/audit.js.map +1 -1
- package/dist/credentials.d.ts +27 -0
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +61 -0
- package/dist/credentials.js.map +1 -1
- package/dist/index.d.ts +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/login.d.ts +63 -6
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +90 -10
- package/dist/login.js.map +1 -1
- package/dist/mfa.d.ts +1 -1
- package/dist/mfa.d.ts.map +1 -1
- package/dist/mfa.js +3 -1
- package/dist/mfa.js.map +1 -1
- package/dist/recovery-codes.d.ts +25 -0
- package/dist/recovery-codes.d.ts.map +1 -0
- package/dist/recovery-codes.js +56 -0
- package/dist/recovery-codes.js.map +1 -0
- package/dist/resets.d.ts +15 -1
- package/dist/resets.d.ts.map +1 -1
- package/dist/resets.js +10 -0
- package/dist/resets.js.map +1 -1
- package/dist/sessions.d.ts +24 -0
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +25 -4
- package/dist/sessions.js.map +1 -1
- package/dist/store.d.ts +3 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +4 -1
- package/dist/store.js.map +1 -1
- package/dist/tables.d.ts +17 -0
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +129 -1
- package/dist/tables.js.map +1 -1
- package/dist/types.d.ts +89 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -1
- package/dist/types.js.map +1 -1
- package/dist/user-agent.d.ts +18 -0
- package/dist/user-agent.d.ts.map +1 -0
- package/dist/user-agent.js +39 -0
- package/dist/user-agent.js.map +1 -0
- package/dist/users.d.ts +24 -1
- package/dist/users.d.ts.map +1 -1
- package/dist/users.js +72 -3
- package/dist/users.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { identifier, isCogentaError, sql } from '@cogenta/core';
|
|
2
|
+
import { TABLES } from './tables.js';
|
|
3
|
+
const DEFAULT_FULL_CHECK_INTERVAL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
4
|
+
function checkpointOf(row) {
|
|
5
|
+
if (row.checkpoint_id === null || row.checkpoint_at === null || row.checkpoint_hash === null) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
return { id: row.checkpoint_id, at: row.checkpoint_at, hash: row.checkpoint_hash };
|
|
9
|
+
}
|
|
10
|
+
function statusOf(row) {
|
|
11
|
+
if (row === null) {
|
|
12
|
+
return {
|
|
13
|
+
state: 'never-run',
|
|
14
|
+
checkpoint: null,
|
|
15
|
+
entriesChecked: 0,
|
|
16
|
+
lastCheckedAt: null,
|
|
17
|
+
lastMode: null,
|
|
18
|
+
lastFullCheckedAt: null,
|
|
19
|
+
brokenAt: null,
|
|
20
|
+
brokenEntryId: null,
|
|
21
|
+
brokenMessage: null,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
state: row.state,
|
|
26
|
+
checkpoint: checkpointOf(row),
|
|
27
|
+
entriesChecked: row.entries_checked === null ? 0 : Number(row.entries_checked),
|
|
28
|
+
lastCheckedAt: row.last_checked_at,
|
|
29
|
+
lastMode: row.last_mode,
|
|
30
|
+
lastFullCheckedAt: row.last_full_checked_at,
|
|
31
|
+
brokenAt: row.broken_at,
|
|
32
|
+
brokenEntryId: row.broken_entry_id,
|
|
33
|
+
brokenMessage: row.broken_message,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function entryIdOf(details) {
|
|
37
|
+
const value = details?.entryId;
|
|
38
|
+
return typeof value === 'string' ? value : null;
|
|
39
|
+
}
|
|
40
|
+
export function createAuditIntegrityStore(db, audit, options = {}) {
|
|
41
|
+
const table = identifier(TABLES.auditIntegrity, db.dialect);
|
|
42
|
+
const now = options.now ?? Date.now;
|
|
43
|
+
const fullCheckIntervalMs = options.fullCheckIntervalMs ?? DEFAULT_FULL_CHECK_INTERVAL_MS;
|
|
44
|
+
async function readRow() {
|
|
45
|
+
const result = await db.query(sql `select * from ${table} where id = ${'singleton'}`);
|
|
46
|
+
return result.rows[0] ?? null;
|
|
47
|
+
}
|
|
48
|
+
async function writeRow(row) {
|
|
49
|
+
await db.transaction(async (tx) => {
|
|
50
|
+
await tx.query(sql `delete from ${table} where id = ${'singleton'}`);
|
|
51
|
+
await tx.query(sql `
|
|
52
|
+
insert into ${table} (
|
|
53
|
+
id, state, checkpoint_id, checkpoint_at, checkpoint_hash, entries_checked,
|
|
54
|
+
last_checked_at, last_mode, last_full_checked_at, broken_at, broken_entry_id, broken_message
|
|
55
|
+
) values (
|
|
56
|
+
${'singleton'}, ${row.state}, ${row.checkpoint_id}, ${row.checkpoint_at}, ${row.checkpoint_hash},
|
|
57
|
+
${row.entries_checked}, ${row.last_checked_at}, ${row.last_mode}, ${row.last_full_checked_at},
|
|
58
|
+
${row.broken_at}, ${row.broken_entry_id}, ${row.broken_message}
|
|
59
|
+
)`);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function dueForFullCheck(row) {
|
|
63
|
+
if (row === null || row.last_full_checked_at === null)
|
|
64
|
+
return true;
|
|
65
|
+
return now() - Date.parse(row.last_full_checked_at) >= fullCheckIntervalMs;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
status: async () => statusOf(await readRow()),
|
|
69
|
+
check: async (checkOptions = {}) => {
|
|
70
|
+
const row = await readRow();
|
|
71
|
+
const nowIso = new Date(now()).toISOString();
|
|
72
|
+
// Once broken, a plain (unforced) tick only re-stamps that it looked —
|
|
73
|
+
// it does not retry, because nothing about retrying a full or
|
|
74
|
+
// incremental verify would change the outcome until a human has acted
|
|
75
|
+
// (typically `prune()`ing the bad prefix, or restoring from a backup).
|
|
76
|
+
if (row !== null && row.state === 'broken' && checkOptions.full !== true) {
|
|
77
|
+
const { id: _id, ...rest } = row;
|
|
78
|
+
const restamped = { ...rest, last_checked_at: nowIso };
|
|
79
|
+
await writeRow(restamped);
|
|
80
|
+
return { status: statusOf({ ...restamped, id: 'singleton' }), newlyBroken: false };
|
|
81
|
+
}
|
|
82
|
+
const wasBroken = row?.state === 'broken';
|
|
83
|
+
const forceFull = checkOptions.full === true || row === null || dueForFullCheck(row);
|
|
84
|
+
const mode = forceFull ? 'full' : 'incremental';
|
|
85
|
+
const since = forceFull
|
|
86
|
+
? null
|
|
87
|
+
: checkpointOf(row ?? { checkpoint_id: null, checkpoint_at: null, checkpoint_hash: null });
|
|
88
|
+
try {
|
|
89
|
+
const result = await audit.verifyRange(since);
|
|
90
|
+
const checkpoint = result.checkpoint ?? since;
|
|
91
|
+
const nextRow = {
|
|
92
|
+
state: 'ok',
|
|
93
|
+
checkpoint_id: checkpoint?.id ?? null,
|
|
94
|
+
checkpoint_at: checkpoint?.at ?? null,
|
|
95
|
+
checkpoint_hash: checkpoint?.hash ?? null,
|
|
96
|
+
entries_checked: String(result.entriesChecked),
|
|
97
|
+
last_checked_at: nowIso,
|
|
98
|
+
last_mode: mode,
|
|
99
|
+
last_full_checked_at: mode === 'full' ? nowIso : (row?.last_full_checked_at ?? null),
|
|
100
|
+
broken_at: null,
|
|
101
|
+
broken_entry_id: null,
|
|
102
|
+
broken_message: null,
|
|
103
|
+
};
|
|
104
|
+
await writeRow(nextRow);
|
|
105
|
+
return { status: statusOf({ ...nextRow, id: 'singleton' }), newlyBroken: false };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (!isCogentaError(error) || error.code !== 'AUDIT_CHAIN_BROKEN')
|
|
109
|
+
throw error;
|
|
110
|
+
const nextRow = {
|
|
111
|
+
state: 'broken',
|
|
112
|
+
// Frozen at the last point known good — never advanced past a break.
|
|
113
|
+
checkpoint_id: row?.checkpoint_id ?? null,
|
|
114
|
+
checkpoint_at: row?.checkpoint_at ?? null,
|
|
115
|
+
checkpoint_hash: row?.checkpoint_hash ?? null,
|
|
116
|
+
entries_checked: row?.entries_checked ?? '0',
|
|
117
|
+
last_checked_at: nowIso,
|
|
118
|
+
last_mode: mode,
|
|
119
|
+
last_full_checked_at: row?.last_full_checked_at ?? null,
|
|
120
|
+
broken_at: nowIso,
|
|
121
|
+
broken_entry_id: entryIdOf(error.details),
|
|
122
|
+
broken_message: error.message,
|
|
123
|
+
};
|
|
124
|
+
await writeRow(nextRow);
|
|
125
|
+
return { status: statusOf({ ...nextRow, id: 'singleton' }), newlyBroken: !wasBroken };
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=audit-integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-integrity.js","sourceRoot":"","sources":["../src/audit-integrity.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.d.ts
CHANGED
|
@@ -1,17 +1,93 @@
|
|
|
1
1
|
import { type DatabaseHandle } from '@cogenta/core';
|
|
2
|
-
import type { AuditEntry, RecordAuditInput } from './types.js';
|
|
2
|
+
import type { AuditActorKind, AuditEntry, RecordAuditInput } from './types.js';
|
|
3
3
|
export interface AuditFilter {
|
|
4
4
|
readonly actorId?: string;
|
|
5
5
|
readonly action?: string;
|
|
6
6
|
readonly collection?: string;
|
|
7
|
+
/** Lower bound on `at`, inclusive. */
|
|
7
8
|
readonly since?: string;
|
|
9
|
+
/** Upper bound on `at`, inclusive — the other half of task 2's date-range filter. */
|
|
10
|
+
readonly until?: string;
|
|
11
|
+
/** Task 4: narrows to one origin. See `classifyAuditActor`. */
|
|
12
|
+
readonly actorKind?: AuditActorKind;
|
|
8
13
|
readonly limit?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Cursor pagination (fiche 67 task 1): strictly older than this
|
|
16
|
+
* `(at, id)` position in the `order by at desc, id desc` listing — the
|
|
17
|
+
* exact position the previous page's last row sat at. Same shape as the
|
|
18
|
+
* chain-verification checkpoint (`AuditChainPoint`) on purpose, but this
|
|
19
|
+
* is a *listing* cursor, never confused with a verified chain position:
|
|
20
|
+
* an unknown or stale `before` (the row it named was pruned since) simply
|
|
21
|
+
* yields an earlier page, the same "not a security boundary" behaviour
|
|
22
|
+
* `users-router.ts`'s cursor already documents.
|
|
23
|
+
*/
|
|
24
|
+
readonly before?: {
|
|
25
|
+
readonly at: string;
|
|
26
|
+
readonly id: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A point in the chain a caller has already verified up to — everything
|
|
31
|
+
* `verifyRange` needs to resume from it instead of the start.
|
|
32
|
+
*/
|
|
33
|
+
export interface AuditChainPoint {
|
|
34
|
+
readonly id: string;
|
|
35
|
+
readonly at: string;
|
|
36
|
+
readonly hash: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AuditVerifyRangeResult {
|
|
39
|
+
/**
|
|
40
|
+
* The newest point now verified. `since` unchanged when nothing new was
|
|
41
|
+
* found; `null` only when the whole chain (or the whole range) is empty.
|
|
42
|
+
*/
|
|
43
|
+
readonly checkpoint: AuditChainPoint | null;
|
|
44
|
+
readonly entriesChecked: number;
|
|
45
|
+
}
|
|
46
|
+
export interface AuditPruneResult {
|
|
47
|
+
readonly prunedCount: number;
|
|
48
|
+
/** The new genesis anchor — `null` only when nothing matched `olderThan`. */
|
|
49
|
+
readonly genesis: AuditChainPoint | null;
|
|
9
50
|
}
|
|
10
51
|
export interface AuditLog {
|
|
11
52
|
record(input: RecordAuditInput): Promise<AuditEntry>;
|
|
12
53
|
list(filter?: AuditFilter): Promise<readonly AuditEntry[]>;
|
|
13
|
-
/**
|
|
54
|
+
/** One entry by id, or `null` — the detail view's lookup (fiche 21 task 1). */
|
|
55
|
+
get(id: string): Promise<AuditEntry | null>;
|
|
56
|
+
/** Recomputes every hash and compares, from the oldest surviving entry. Throws `AUDIT_CHAIN_BROKEN` naming the first mismatch. */
|
|
14
57
|
verify(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* The bounded form `verify()` is built on: recomputes only entries at or
|
|
60
|
+
* after `since` (the whole surviving chain when `since` is `null`), after
|
|
61
|
+
* first confirming `since` itself still matches what is stored — an
|
|
62
|
+
* altered or vanished checkpoint is exactly as much a break as an altered
|
|
63
|
+
* row in the middle ever was, unless it vanished because of a recorded
|
|
64
|
+
* `prune()`, which is not tampering and must not be reported as some.
|
|
65
|
+
*/
|
|
66
|
+
verifyRange(since: AuditChainPoint | null): Promise<AuditVerifyRangeResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Deletes every entry strictly older than `olderThan` (ISO-8601), for
|
|
69
|
+
* real — fiche 21 task 5's GDPR exit. Refuses (throwing
|
|
70
|
+
* `AUDIT_CHAIN_BROKEN`) if the segment about to be removed does not
|
|
71
|
+
* itself verify first: purging into an already-broken chain would erase
|
|
72
|
+
* the evidence of the break, not just old rows.
|
|
73
|
+
*/
|
|
74
|
+
prune(olderThan: string): Promise<AuditPruneResult>;
|
|
15
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Who or what an entry names, read from signals the log already carries —
|
|
78
|
+
* no schema change needed to answer "human or agent or key or system"
|
|
79
|
+
* (fiche 21 task 4, "vérifier ce que le modèle porte déjà avant d'ajouter un
|
|
80
|
+
* champ"):
|
|
81
|
+
* - `actorId === null` → `'system'` (nobody was signed in — a migration, a
|
|
82
|
+
* scheduled job).
|
|
83
|
+
* - `actorId` starts with `apikey:` → `'api_key'`. `resolveActor`
|
|
84
|
+
* (`packages/api/src/rest/auth-router.ts`) has prefixed every API-key
|
|
85
|
+
* actor's id this way since L13 task 8; nothing else produces that shape.
|
|
86
|
+
* - `action` starts with `agent.tool.` → `'agent'`. `withAudit`
|
|
87
|
+
* (`packages/agents/src/audit/with-audit.ts`, L4 task 6) is the only
|
|
88
|
+
* writer that names an action this way.
|
|
89
|
+
* - anything else → `'human'`.
|
|
90
|
+
*/
|
|
91
|
+
export declare function classifyAuditActor(entry: Pick<AuditEntry, 'actorId' | 'action'>): AuditActorKind;
|
|
16
92
|
export declare function createAuditLog(db: DatabaseHandle, now?: () => number): AuditLog;
|
|
17
93
|
//# sourceMappingURL=audit.d.ts.map
|
package/dist/audit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAEzF,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAEzF,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAuF9E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,qFAAqF;IACrF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;;;;;OASG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/D;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAA;IAC3C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAAA;CACzC;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACpD,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAA;IAC1D,+EAA+E;IAC/E,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IAC3C,kIAAkI;IAClI,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB;;;;;;;OAOG;IACH,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAC3E;;;;;;OAMG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACpD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,GAAG,QAAQ,CAAC,GAAG,cAAc,CAKhG;AA+BD,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,QAAQ,CAsMzF"}
|
package/dist/audit.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { CogentaError, identifier, newId, sql } from '@cogenta/core';
|
|
3
3
|
import { TABLES } from './tables.js';
|
|
4
4
|
function fromRow(row) {
|
|
5
|
+
const version = row.version;
|
|
5
6
|
return {
|
|
6
7
|
id: row.id,
|
|
7
8
|
at: row.at,
|
|
@@ -11,10 +12,18 @@ function fromRow(row) {
|
|
|
11
12
|
collection: row.collection_name,
|
|
12
13
|
entryId: row.entry_id,
|
|
13
14
|
diff: row.diff === null ? null : JSON.parse(row.diff),
|
|
15
|
+
version: version === undefined || version === null ? null : Number(version),
|
|
14
16
|
hash: row.hash,
|
|
15
17
|
previousHash: row.previous_hash,
|
|
16
18
|
};
|
|
17
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Deliberately reads only the fields this format has hashed since the very
|
|
22
|
+
* first entry any site has ever recorded: `id`, `at`, `actor_id`,
|
|
23
|
+
* `actor_roles`, `action`, `collection_name`, `entry_id`, `diff`, chained to
|
|
24
|
+
* `previousHash`. Adding a field here changes what every already-recorded
|
|
25
|
+
* hash means — do not, without a migration plan for every existing chain.
|
|
26
|
+
*/
|
|
18
27
|
function computeHash(previousHash, fields) {
|
|
19
28
|
const canonical = JSON.stringify([
|
|
20
29
|
previousHash,
|
|
@@ -29,19 +38,132 @@ function computeHash(previousHash, fields) {
|
|
|
29
38
|
]);
|
|
30
39
|
return createHash('sha256').update(canonical).digest('hex');
|
|
31
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Who or what an entry names, read from signals the log already carries —
|
|
43
|
+
* no schema change needed to answer "human or agent or key or system"
|
|
44
|
+
* (fiche 21 task 4, "vérifier ce que le modèle porte déjà avant d'ajouter un
|
|
45
|
+
* champ"):
|
|
46
|
+
* - `actorId === null` → `'system'` (nobody was signed in — a migration, a
|
|
47
|
+
* scheduled job).
|
|
48
|
+
* - `actorId` starts with `apikey:` → `'api_key'`. `resolveActor`
|
|
49
|
+
* (`packages/api/src/rest/auth-router.ts`) has prefixed every API-key
|
|
50
|
+
* actor's id this way since L13 task 8; nothing else produces that shape.
|
|
51
|
+
* - `action` starts with `agent.tool.` → `'agent'`. `withAudit`
|
|
52
|
+
* (`packages/agents/src/audit/with-audit.ts`, L4 task 6) is the only
|
|
53
|
+
* writer that names an action this way.
|
|
54
|
+
* - anything else → `'human'`.
|
|
55
|
+
*/
|
|
56
|
+
export function classifyAuditActor(entry) {
|
|
57
|
+
if (entry.actorId === null)
|
|
58
|
+
return 'system';
|
|
59
|
+
if (entry.actorId.startsWith('apikey:'))
|
|
60
|
+
return 'api_key';
|
|
61
|
+
if (entry.action.startsWith('agent.tool.'))
|
|
62
|
+
return 'agent';
|
|
63
|
+
return 'human';
|
|
64
|
+
}
|
|
65
|
+
function actorKindCondition(kind) {
|
|
66
|
+
switch (kind) {
|
|
67
|
+
case 'system':
|
|
68
|
+
return sql `actor_id is null`;
|
|
69
|
+
case 'api_key':
|
|
70
|
+
return sql `actor_id like ${'apikey:%'}`;
|
|
71
|
+
case 'agent':
|
|
72
|
+
return sql `action like ${'agent.tool.%'}`;
|
|
73
|
+
case 'human':
|
|
74
|
+
return sql `(actor_id is not null and actor_id not like ${'apikey:%'} and action not like ${'agent.tool.%'})`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function chainBroken(entryId, reason) {
|
|
78
|
+
return reason === 'discontinuous'
|
|
79
|
+
? new CogentaError({
|
|
80
|
+
code: 'AUDIT_CHAIN_BROKEN',
|
|
81
|
+
message: `Audit entry ${entryId} does not chain from the entry before it.`,
|
|
82
|
+
hint: 'The audit log has been edited or reordered outside of record(). Treat every entry from this point on as untrusted.',
|
|
83
|
+
details: { entryId },
|
|
84
|
+
})
|
|
85
|
+
: new CogentaError({
|
|
86
|
+
code: 'AUDIT_CHAIN_BROKEN',
|
|
87
|
+
message: `Audit entry ${entryId} has been modified since it was recorded.`,
|
|
88
|
+
hint: 'Its stored hash no longer matches its content. Treat every entry from this point on as untrusted.',
|
|
89
|
+
details: { entryId },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
32
92
|
export function createAuditLog(db, now = Date.now) {
|
|
33
93
|
const table = identifier(TABLES.auditLog, db.dialect);
|
|
94
|
+
const genesisTable = identifier(TABLES.auditGenesis, db.dialect);
|
|
34
95
|
// Serialises appends within this process. Two concurrent writers computing
|
|
35
96
|
// "the current last hash" from a read that has not committed yet would
|
|
36
97
|
// both chain to the same previous hash and fork the chain — a database
|
|
37
98
|
// transaction alone does not stop that on every dialect's default isolation
|
|
38
99
|
// level, so the invariant is enforced here, once, rather than trusted to
|
|
39
|
-
// whichever caller remembers to lock.
|
|
100
|
+
// whichever caller remembers to lock. `prune()` joins the same queue: a
|
|
101
|
+
// purge running concurrently with an append is exactly the same class of
|
|
102
|
+
// race, just over a delete instead of an insert.
|
|
40
103
|
let appendQueue = Promise.resolve();
|
|
41
104
|
async function lastHash() {
|
|
42
105
|
const result = await db.query(sql `select hash from ${table} order by at desc, id desc limit ${1}`);
|
|
43
106
|
return result.rows[0]?.hash ?? null;
|
|
44
107
|
}
|
|
108
|
+
async function currentGenesis() {
|
|
109
|
+
const result = await db.query(sql `select entry_id, entry_at, entry_hash from ${genesisTable} where id = ${'singleton'}`);
|
|
110
|
+
const row = result.rows[0];
|
|
111
|
+
return row === undefined ? null : { id: row.entry_id, at: row.entry_at, hash: row.entry_hash };
|
|
112
|
+
}
|
|
113
|
+
/** `rows` must already be in `(at, id)` ascending order. */
|
|
114
|
+
function verifyRows(rows, expectedPreviousStart) {
|
|
115
|
+
let expectedPrevious = expectedPreviousStart;
|
|
116
|
+
let last = null;
|
|
117
|
+
for (const row of rows) {
|
|
118
|
+
if (row.previous_hash !== expectedPrevious)
|
|
119
|
+
throw chainBroken(row.id, 'discontinuous');
|
|
120
|
+
const { hash, version: _version, ...rest } = row;
|
|
121
|
+
if (computeHash(row.previous_hash, rest) !== hash)
|
|
122
|
+
throw chainBroken(row.id, 'edited');
|
|
123
|
+
expectedPrevious = hash;
|
|
124
|
+
last = { id: row.id, at: row.at, hash: row.hash };
|
|
125
|
+
}
|
|
126
|
+
return last;
|
|
127
|
+
}
|
|
128
|
+
async function verifyRangeImpl(since) {
|
|
129
|
+
const genesis = await currentGenesis();
|
|
130
|
+
if (since === null) {
|
|
131
|
+
const rows = await db.query(sql `select * from ${table} order by at asc, id asc`);
|
|
132
|
+
const last = verifyRows(rows.rows, genesis?.hash ?? null);
|
|
133
|
+
return { checkpoint: last, entriesChecked: rows.rows.length };
|
|
134
|
+
}
|
|
135
|
+
const found = await db.query(sql `select * from ${table} where id = ${since.id}`);
|
|
136
|
+
const checkpointRow = found.rows[0];
|
|
137
|
+
if (checkpointRow === undefined) {
|
|
138
|
+
// Missing is only innocent when it is exactly what a recorded prune()
|
|
139
|
+
// would leave behind: `since` at or before the current genesis. Newer
|
|
140
|
+
// than the genesis (or no genesis at all) and still missing means
|
|
141
|
+
// something else deleted it — tampering, not retention.
|
|
142
|
+
const explainedByPrune = genesis !== null &&
|
|
143
|
+
(since.at < genesis.at || (since.at === genesis.at && since.id <= genesis.id));
|
|
144
|
+
if (!explainedByPrune)
|
|
145
|
+
throw chainBroken(since.id, 'discontinuous');
|
|
146
|
+
// Resume as if starting fresh from the genesis: everything between the
|
|
147
|
+
// old checkpoint and the genesis is gone by design, and the caller's
|
|
148
|
+
// next checkpoint should be the genesis or later.
|
|
149
|
+
const rows = await db.query(sql `select * from ${table} order by at asc, id asc`);
|
|
150
|
+
const last = verifyRows(rows.rows, genesis?.hash ?? null);
|
|
151
|
+
return { checkpoint: last ?? genesis, entriesChecked: rows.rows.length };
|
|
152
|
+
}
|
|
153
|
+
if (checkpointRow.hash !== since.hash)
|
|
154
|
+
throw chainBroken(since.id, 'edited');
|
|
155
|
+
// Defence in depth: the checkpoint row's own stored hash must still
|
|
156
|
+
// match its own content, not only match what the caller remembered.
|
|
157
|
+
const { hash, version: _version, ...rest } = checkpointRow;
|
|
158
|
+
if (computeHash(checkpointRow.previous_hash, rest) !== hash) {
|
|
159
|
+
throw chainBroken(since.id, 'edited');
|
|
160
|
+
}
|
|
161
|
+
const rows = await db.query(sql `select * from ${table}
|
|
162
|
+
where (at > ${since.at}) or (at = ${since.at} and id > ${since.id})
|
|
163
|
+
order by at asc, id asc`);
|
|
164
|
+
const last = verifyRows(rows.rows, since.hash);
|
|
165
|
+
return { checkpoint: last ?? since, entriesChecked: rows.rows.length };
|
|
166
|
+
}
|
|
45
167
|
return {
|
|
46
168
|
record: (input) => {
|
|
47
169
|
const task = appendQueue.then(async () => {
|
|
@@ -58,11 +180,12 @@ export function createAuditLog(db, now = Date.now) {
|
|
|
58
180
|
previous_hash: previousHash,
|
|
59
181
|
};
|
|
60
182
|
const hash = computeHash(previousHash, base);
|
|
183
|
+
const version = input.version === undefined ? null : String(input.version);
|
|
61
184
|
await db.query(sql `
|
|
62
|
-
insert into ${table} (id, at, actor_id, actor_roles, action, collection_name, entry_id, diff, hash, previous_hash)
|
|
185
|
+
insert into ${table} (id, at, actor_id, actor_roles, action, collection_name, entry_id, diff, hash, previous_hash, version)
|
|
63
186
|
values (${base.id}, ${base.at}, ${base.actor_id}, ${base.actor_roles}, ${base.action},
|
|
64
|
-
${base.collection_name}, ${base.entry_id}, ${base.diff}, ${hash}, ${base.previous_hash})`);
|
|
65
|
-
return fromRow({ ...base, hash });
|
|
187
|
+
${base.collection_name}, ${base.entry_id}, ${base.diff}, ${hash}, ${base.previous_hash}, ${version})`);
|
|
188
|
+
return fromRow({ ...base, hash, version });
|
|
66
189
|
});
|
|
67
190
|
// The queue must advance even if this append fails, or every append
|
|
68
191
|
// after a failure would wait on a promise that never resolves.
|
|
@@ -79,34 +202,55 @@ export function createAuditLog(db, now = Date.now) {
|
|
|
79
202
|
conditions.push(sql `collection_name = ${filter.collection}`);
|
|
80
203
|
if (filter.since !== undefined)
|
|
81
204
|
conditions.push(sql `at >= ${filter.since}`);
|
|
205
|
+
if (filter.until !== undefined)
|
|
206
|
+
conditions.push(sql `at <= ${filter.until}`);
|
|
207
|
+
if (filter.actorKind !== undefined)
|
|
208
|
+
conditions.push(actorKindCondition(filter.actorKind));
|
|
209
|
+
if (filter.before !== undefined) {
|
|
210
|
+
conditions.push(sql `(at < ${filter.before.at} or (at = ${filter.before.at} and id < ${filter.before.id}))`);
|
|
211
|
+
}
|
|
82
212
|
const where = conditions.reduce((left, right) => sql `${left} and ${right}`);
|
|
83
213
|
const result = await db.query(sql `
|
|
84
214
|
select * from ${table} where ${where} order by at desc, id desc limit ${filter.limit ?? 200}`);
|
|
85
215
|
return result.rows.map(fromRow);
|
|
86
216
|
},
|
|
217
|
+
get: async (id) => {
|
|
218
|
+
const result = await db.query(sql `select * from ${table} where id = ${id}`);
|
|
219
|
+
const row = result.rows[0];
|
|
220
|
+
return row === undefined ? null : fromRow(row);
|
|
221
|
+
},
|
|
87
222
|
verify: async () => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
details: { entryId: row.id },
|
|
97
|
-
});
|
|
223
|
+
await verifyRangeImpl(null);
|
|
224
|
+
},
|
|
225
|
+
verifyRange: (since) => verifyRangeImpl(since),
|
|
226
|
+
prune: (olderThan) => {
|
|
227
|
+
const task = appendQueue.then(async () => {
|
|
228
|
+
const candidates = await db.query(sql `select * from ${table} where at < ${olderThan} order by at asc, id asc`);
|
|
229
|
+
if (candidates.rows.length === 0) {
|
|
230
|
+
return { prunedCount: 0, genesis: await currentGenesis() };
|
|
98
231
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
232
|
+
const genesis = await currentGenesis();
|
|
233
|
+
// Throws `AUDIT_CHAIN_BROKEN` when the segment about to be removed
|
|
234
|
+
// is already discontinuous or edited — refusing to purge over
|
|
235
|
+
// evidence of tampering, rather than quietly making it unrecoverable.
|
|
236
|
+
const anchor = verifyRows(candidates.rows, genesis?.hash ?? null);
|
|
237
|
+
if (anchor === null) {
|
|
238
|
+
// Unreachable: `candidates.rows.length > 0` guarantees `verifyRows`
|
|
239
|
+
// returns the last row it processed. Narrows the type for below.
|
|
240
|
+
throw chainBroken('unknown', 'discontinuous');
|
|
107
241
|
}
|
|
108
|
-
|
|
109
|
-
|
|
242
|
+
await db.transaction(async (tx) => {
|
|
243
|
+
await tx.query(sql `delete from ${table} where at < ${olderThan}`);
|
|
244
|
+
await tx.query(sql `delete from ${genesisTable} where id = ${'singleton'}`);
|
|
245
|
+
await tx.query(sql `
|
|
246
|
+
insert into ${genesisTable} (id, entry_id, entry_at, entry_hash, pruned_count, pruned_at)
|
|
247
|
+
values (${'singleton'}, ${anchor.id}, ${anchor.at}, ${anchor.hash},
|
|
248
|
+
${String(candidates.rows.length)}, ${new Date(now()).toISOString()})`);
|
|
249
|
+
});
|
|
250
|
+
return { prunedCount: candidates.rows.length, genesis: anchor };
|
|
251
|
+
});
|
|
252
|
+
appendQueue = task.catch(() => undefined);
|
|
253
|
+
return task;
|
|
110
254
|
},
|
|
111
255
|
};
|
|
112
256
|
}
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAuB,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
|
package/dist/credentials.d.ts
CHANGED
|
@@ -26,6 +26,33 @@ export interface CredentialStore {
|
|
|
26
26
|
data: WebAuthnCredentialData;
|
|
27
27
|
} | null>;
|
|
28
28
|
updateWebAuthnCounter(credentialId: string, counter: number): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Replaces this account's recovery codes wholesale with `hashes` — already
|
|
31
|
+
* hashed by the caller (`recovery-codes.ts`), never a plaintext code. Used
|
|
32
|
+
* both for the first batch (TOTP confirmation) and for regeneration, which
|
|
33
|
+
* is exactly what makes regeneration invalidate the old ones: there is
|
|
34
|
+
* nothing left to consume the previous batch against.
|
|
35
|
+
*/
|
|
36
|
+
setRecoveryCodes(userId: string, hashes: readonly string[]): Promise<void>;
|
|
37
|
+
/** How many codes exist and how many are still unused. `null` when none were ever issued. */
|
|
38
|
+
recoveryCodesStatus(userId: string): Promise<{
|
|
39
|
+
readonly total: number;
|
|
40
|
+
readonly remaining: number;
|
|
41
|
+
} | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Checks `code` against every unused code for this account and, on a
|
|
44
|
+
* match, marks that one used. Single use is enforced with a
|
|
45
|
+
* compare-and-set on the write (`resets.ts`'s `markUsed` idiom): the
|
|
46
|
+
* `update` is conditioned on the row still holding the exact bytes this
|
|
47
|
+
* call read, so a concurrent redemption that wrote first makes this one's
|
|
48
|
+
* `update` affect zero rows, which is retried against the fresher row
|
|
49
|
+
* rather than blindly overwriting it. Two simultaneous calls with the same
|
|
50
|
+
* code can therefore never both return `true`. Returns `false` for no
|
|
51
|
+
* account, no codes, or no match at all.
|
|
52
|
+
*/
|
|
53
|
+
consumeRecoveryCode(userId: string, code: string): Promise<boolean>;
|
|
54
|
+
/** Deletes every recovery code for this account — there is nothing left to be a spare key for once TOTP itself is off. */
|
|
55
|
+
removeRecoveryCodes(userId: string): Promise<void>;
|
|
29
56
|
/** What second factors, if any, this user has set up. */
|
|
30
57
|
kinds(userId: string): Promise<readonly CredentialKind[]>;
|
|
31
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAIzF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAUhD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAClE,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAE7C,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,kFAAkF;IAClF,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACjF,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEzC,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClF,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,sBAAsB,EAAE,CAAC,CAAA;IAC/E,8BAA8B,CAC5B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,sBAAsB,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACnE,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAE3E;;;;;;OAMG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1E,6FAA6F;IAC7F,mBAAmB,CACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACzE;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACnE,0HAA0H;IAC1H,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAElD,yDAAyD;IACzD,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC,CAAA;CAC1D;AAUD,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,cAAc,EAClB,GAAG,GAAE,MAAM,MAAiB,GAC3B,eAAe,CAoLjB"}
|