@coffre/vault 0.10.1 → 0.11.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.
- package/dist/cloudflare.d.ts +1 -1
- package/dist/cloudflare.js +2 -2
- package/dist/{index-CByXzyi-.d.ts → index-zkW6DEm9.d.ts} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/{src-D82jyprb.js → src-B0d-W-SI.js} +4 -3
- package/dist/{vault-B0YiPBgi.js → vault-DEjPmLNt.js} +590 -192
- package/package.json +3 -3
- package/src/config.ts +6 -5
- package/src/decide.ts +198 -0
- package/src/snapshot.postgres.ts +97 -0
- package/src/snapshot.sqlite.ts +103 -0
- package/src/snapshot.ts +191 -0
- package/src/store.ts +28 -37
- package/src/vault.ts +189 -183
package/src/store.ts
CHANGED
|
@@ -88,10 +88,10 @@ function memberColumns(db: Queryable) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/** A row as stored: `role` and `scope` as their columns hold them. */
|
|
91
|
-
type StoredMember = Omit<Member, 'role' | 'scope'> & { owner: boolean; role: string | null; scope: string | null };
|
|
91
|
+
export type StoredMember = Omit<Member, 'role' | 'scope'> & { owner: boolean; role: string | null; scope: string | null };
|
|
92
92
|
|
|
93
93
|
/** A member as their row says (`storedRole`). */
|
|
94
|
-
function memberOf({ owner, role, scope, ...row }: StoredMember): Member {
|
|
94
|
+
export function memberOf({ owner, role, scope, ...row }: StoredMember): Member {
|
|
95
95
|
return { ...row, ...storedRole({ owner, role, scope }) };
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -308,38 +308,6 @@ export async function everyProjectHolders(db: Queryable): Promise<string[]> {
|
|
|
308
308
|
|
|
309
309
|
// --- the log --------------------------------------------------------------------
|
|
310
310
|
|
|
311
|
-
/** The action of an entry that releases a key, which the bulk limit counts. */
|
|
312
|
-
export const RELEASE = 'secret.read';
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* How many keys the vault has released to `principal` since `after`: one
|
|
316
|
-
* for an entry about one secret, and as many as it lists for one about
|
|
317
|
-
* several (`secrets`), a read logged once per place.
|
|
318
|
-
*/
|
|
319
|
-
export async function releasesSince(db: Queryable, principal: string, after: number): Promise<number> {
|
|
320
|
-
const [{ n }] = await released(db, { principal, after });
|
|
321
|
-
return Number(n ?? 0);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const released = prepared((db) => {
|
|
325
|
-
const { auditLog } = tablesOf(db);
|
|
326
|
-
const listed = engineOf(db) === 'postgres'
|
|
327
|
-
? sql`jsonb_array_length((${auditLog.metadata})::jsonb -> 'secrets')`
|
|
328
|
-
: sql`json_array_length(${auditLog.metadata}, '$.secrets')`;
|
|
329
|
-
return db
|
|
330
|
-
.select({ n: sql<string | number | null>`SUM(COALESCE(${listed}, 1))` })
|
|
331
|
-
.from(auditLog)
|
|
332
|
-
.where(
|
|
333
|
-
and(
|
|
334
|
-
eq(auditLog.author, 'vault'),
|
|
335
|
-
eq(auditLog.actor, sql.placeholder('principal')),
|
|
336
|
-
eq(auditLog.action, RELEASE),
|
|
337
|
-
eq(auditLog.decision, 'allow'),
|
|
338
|
-
gt(auditLog.occurredAt, sql.placeholder('after')),
|
|
339
|
-
),
|
|
340
|
-
);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
311
|
/** Rows of the log as the chain's codec reads them: `author` and `decision` are checked by the table. */
|
|
344
312
|
function stored(rows: object[]): StoredEntry[] {
|
|
345
313
|
return rows as StoredEntry[];
|
|
@@ -504,8 +472,10 @@ export async function natures(db: Queryable, secretIds: readonly string[]): Prom
|
|
|
504
472
|
|
|
505
473
|
/**
|
|
506
474
|
* For each version a rewrap would bring back into its secret, its nature
|
|
507
|
-
*
|
|
508
|
-
*
|
|
475
|
+
* when it is an old value, no looser than it was stored nor than its value
|
|
476
|
+
* was made since (the newest version of its very ciphertext), not the
|
|
477
|
+
* secret's current one; null for the current one, which a mark wraps again
|
|
478
|
+
* with no value changing.
|
|
509
479
|
*/
|
|
510
480
|
export async function restoredNatures(
|
|
511
481
|
db: Queryable,
|
|
@@ -515,7 +485,11 @@ export async function restoredNatures(
|
|
|
515
485
|
const { secrets, secretVersions } = tablesOf(db);
|
|
516
486
|
const [current, versions] = await Promise.all([
|
|
517
487
|
db.select({ id: secrets.id, version: secrets.currentVersionId }).from(secrets).where(inArray(secrets.id, [...new Set(items.map((item) => item.secretId))])),
|
|
518
|
-
db.select({
|
|
488
|
+
db.select({
|
|
489
|
+
id: secretVersions.id,
|
|
490
|
+
sensitive: sql<boolean | number>`(${secretVersions.sensitive} OR (${heldAs(secretVersions, sql`same.sensitive`)}))`,
|
|
491
|
+
plain: sql<boolean | number>`(${secretVersions.plaintext} IS NOT NULL AND (${heldAs(secretVersions, sql`same.plaintext IS NOT NULL`)}))`,
|
|
492
|
+
}).from(secretVersions)
|
|
519
493
|
.where(inArray(secretVersions.id, [...new Set(items.map((item) => item.from))])),
|
|
520
494
|
]);
|
|
521
495
|
const currentOf = new Map(current.map((row) => [row.id, row.version]));
|
|
@@ -698,6 +672,12 @@ export type SecretVersion = {
|
|
|
698
672
|
current: boolean;
|
|
699
673
|
/** Whether its secret's newest version is sensitive: what a rewrap of it may not loosen. */
|
|
700
674
|
newestSensitive: boolean;
|
|
675
|
+
/**
|
|
676
|
+
* Whether its value was made sensitive since: the newest version of its
|
|
677
|
+
* secret with its very ciphertext, which a mark in place or a restore wraps
|
|
678
|
+
* again unchanged, is. What a rewrap of it may not loosen either.
|
|
679
|
+
*/
|
|
680
|
+
heldSensitive: boolean;
|
|
701
681
|
};
|
|
702
682
|
|
|
703
683
|
export async function versions(db: Queryable, ids: readonly string[]): Promise<SecretVersion[]> {
|
|
@@ -723,6 +703,7 @@ export async function versions(db: Queryable, ids: readonly string[]): Promise<S
|
|
|
723
703
|
// The newest by the versions' own order: the app may write `current_version_id`, never a version.
|
|
724
704
|
newest: sql<number | string>`(SELECT max(newest.version) FROM secret_versions newest WHERE newest.secret_id = ${secretVersions.secretId})`,
|
|
725
705
|
newestSensitive: sql<boolean | number>`(SELECT newest.sensitive FROM secret_versions newest WHERE newest.secret_id = ${secretVersions.secretId} ORDER BY newest.version DESC LIMIT 1)`,
|
|
706
|
+
heldSensitive: sql<boolean | number>`(${heldAs(secretVersions, sql`same.sensitive`)})`,
|
|
726
707
|
})
|
|
727
708
|
.from(secretVersions)
|
|
728
709
|
.innerJoin(secrets, eq(secrets.id, secretVersions.secretId))
|
|
@@ -749,5 +730,15 @@ export async function versions(db: Queryable, ids: readonly string[]): Promise<S
|
|
|
749
730
|
current: row.currentVersionId === row.id && Number(row.newest) === row.version,
|
|
750
731
|
// SQLite answers a subquery's boolean as a number.
|
|
751
732
|
newestSensitive: Boolean(row.newestSensitive),
|
|
733
|
+
heldSensitive: Boolean(row.heldSensitive),
|
|
752
734
|
}));
|
|
753
735
|
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* `column` of the newest version of a version's secret with its very ciphertext: its value, as it is held now. Known
|
|
739
|
+
* by its IV and tag, which under one data key name one ciphertext, and which, unlike the ciphertext, no erasure empties.
|
|
740
|
+
*/
|
|
741
|
+
function heldAs(secretVersions: ReturnType<typeof tablesOf>['secretVersions'], column: SQL): SQL {
|
|
742
|
+
return sql`SELECT ${column} FROM secret_versions same WHERE same.secret_id = ${secretVersions.secretId}
|
|
743
|
+
AND same.iv = ${secretVersions.iv} AND same.auth_tag = ${secretVersions.authTag} ORDER BY same.version DESC LIMIT 1`;
|
|
744
|
+
}
|