@bevel-software/platform-core-backend 0.23.0 → 0.25.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/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +17 -3
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +3 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +25 -1
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/core/lifecycle.d.ts +12 -0
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +10 -0
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core-config.d.ts +16 -0
- package/dist/core-config.d.ts.map +1 -1
- package/dist/core-config.js +17 -0
- package/dist/core-config.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +4 -1
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access/directory-sync-bot.d.ts.map +1 -1
- package/dist/modules/access/directory-sync-bot.js +7 -3
- package/dist/modules/access/directory-sync-bot.js.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts +8 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.d.ts.map +1 -1
- package/dist/modules/agent-instructions/agent-instructions.routes.js +8 -2
- package/dist/modules/agent-instructions/agent-instructions.routes.js.map +1 -1
- package/dist/modules/agent-instructions/compose.d.ts +38 -6
- package/dist/modules/agent-instructions/compose.d.ts.map +1 -1
- package/dist/modules/agent-instructions/compose.js +39 -6
- package/dist/modules/agent-instructions/compose.js.map +1 -1
- package/dist/modules/agent-instructions/index.d.ts +2 -1
- package/dist/modules/agent-instructions/index.d.ts.map +1 -1
- package/dist/modules/agent-instructions/index.js +2 -1
- package/dist/modules/agent-instructions/index.js.map +1 -1
- package/dist/modules/agent-instructions/shared-file-rules.d.ts +115 -0
- package/dist/modules/agent-instructions/shared-file-rules.d.ts.map +1 -0
- package/dist/modules/agent-instructions/shared-file-rules.js +272 -0
- package/dist/modules/agent-instructions/shared-file-rules.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +4 -2
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/auth/account-erasure.service.d.ts.map +1 -1
- package/dist/modules/auth/account-erasure.service.js +57 -16
- package/dist/modules/auth/account-erasure.service.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +25 -15
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.d.ts +20 -2
- package/dist/modules/code-mode/code-mode.tool.d.ts.map +1 -1
- package/dist/modules/code-mode/code-mode.tool.js +66 -35
- package/dist/modules/code-mode/code-mode.tool.js.map +1 -1
- package/dist/modules/database/connection.d.ts +16 -0
- package/dist/modules/database/connection.d.ts.map +1 -1
- package/dist/modules/database/connection.js +117 -0
- package/dist/modules/database/connection.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +296 -96
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +81 -31
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/database/migrate.d.ts +95 -1
- package/dist/modules/database/migrate.d.ts.map +1 -1
- package/dist/modules/database/migrate.js +390 -2
- package/dist/modules/database/migrate.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts +17 -0
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +29 -0
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +8 -0
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +38 -8
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/plugins/join-request-records.store.d.ts.map +1 -1
- package/dist/modules/plugins/join-request-records.store.js +7 -4
- package/dist/modules/plugins/join-request-records.store.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +8 -2
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +30 -0
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.tools.js +42 -31
- package/dist/modules/tool-manuals/tool-manuals.tools.js.map +1 -1
- package/dist/modules/tool-registry/description-length.d.ts +80 -0
- package/dist/modules/tool-registry/description-length.d.ts.map +1 -0
- package/dist/modules/tool-registry/description-length.js +108 -0
- package/dist/modules/tool-registry/description-length.js.map +1 -0
- package/dist/modules/workflow/git/git.service.d.ts +25 -0
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +40 -1
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/pending-commits.service.d.ts.map +1 -1
- package/dist/modules/workflow/pending-commits.service.js +5 -1
- package/dist/modules/workflow/pending-commits.service.js.map +1 -1
- package/dist/modules/workflow/recovery-bot.d.ts.map +1 -1
- package/dist/modules/workflow/recovery-bot.js +7 -3
- package/dist/modules/workflow/recovery-bot.js.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/review-workflow/review-workflow.service.js +12 -3
- package/dist/modules/workflow/review-workflow/review-workflow.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +4 -1
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/agent-upload.routes.d.ts +77 -0
- package/dist/modules/workspace/agent-upload.routes.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.routes.js +210 -0
- package/dist/modules/workspace/agent-upload.routes.js.map +1 -0
- package/dist/modules/workspace/agent-upload.store.d.ts +284 -0
- package/dist/modules/workspace/agent-upload.store.d.ts.map +1 -0
- package/dist/modules/workspace/agent-upload.store.js +553 -0
- package/dist/modules/workspace/agent-upload.store.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +3 -3
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.d.ts +40 -0
- package/dist/modules/workspace/startup/steps/template-source.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-source.js +46 -4
- package/dist/modules/workspace/startup/steps/template-source.js.map +1 -1
- package/dist/modules/workspace/upload-limits.d.ts +13 -0
- package/dist/modules/workspace/upload-limits.d.ts.map +1 -0
- package/dist/modules/workspace/upload-limits.js +13 -0
- package/dist/modules/workspace/upload-limits.js.map +1 -0
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +1 -1
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +13 -0
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +61 -33
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/modules/workspace/workspace.tools.d.ts +11 -9
- package/dist/modules/workspace/workspace.tools.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.tools.js +570 -134
- package/dist/modules/workspace/workspace.tools.js.map +1 -1
- package/dist/modules/workspace/write-denial.d.ts +0 -6
- package/dist/modules/workspace/write-denial.d.ts.map +1 -1
- package/dist/modules/workspace/write-denial.js +0 -6
- package/dist/modules/workspace/write-denial.js.map +1 -1
- package/dist/modules/workspace/zip-entry-rules.d.ts +114 -0
- package/dist/modules/workspace/zip-entry-rules.d.ts.map +1 -0
- package/dist/modules/workspace/zip-entry-rules.js +154 -0
- package/dist/modules/workspace/zip-entry-rules.js.map +1 -0
- package/dist/shared/column-crypto.d.ts +194 -0
- package/dist/shared/column-crypto.d.ts.map +1 -0
- package/dist/shared/column-crypto.js +144 -0
- package/dist/shared/column-crypto.js.map +1 -0
- package/dist/shared/token-crypto.d.ts.map +1 -1
- package/dist/shared/token-crypto.js +25 -1
- package/dist/shared/token-crypto.js.map +1 -1
- package/dist/tenancy/static-tenant-source.d.ts.map +1 -1
- package/dist/tenancy/static-tenant-source.js +1 -0
- package/dist/tenancy/static-tenant-source.js.map +1 -1
- package/dist/tenancy/tenant-secrets.d.ts +5 -1
- package/dist/tenancy/tenant-secrets.d.ts.map +1 -1
- package/dist/tenancy/tenant-secrets.js +4 -0
- package/dist/tenancy/tenant-secrets.js.map +1 -1
- package/kb-template/AGENTS.md +42 -0
- package/migrations/0016_pii_encryption.sql +20 -0
- package/migrations/meta/0016_snapshot.json +2327 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +3 -3
- package/src/core/__tests__/lifecycle.test.ts +71 -4
- package/src/core/create-core-server.ts +21 -3
- package/src/core/create-core-services.ts +27 -1
- package/src/core/lifecycle.ts +19 -0
- package/src/core-config.ts +18 -0
- package/src/index.ts +25 -0
- package/src/modules/access/__tests__/users-db-double.ts +21 -12
- package/src/modules/access/access.routes.ts +4 -1
- package/src/modules/access/directory-sync-bot.ts +7 -3
- package/src/modules/agent-instructions/__tests__/agent-instructions.route.test.ts +8 -4
- package/src/modules/agent-instructions/__tests__/compose.test.ts +19 -10
- package/src/modules/agent-instructions/__tests__/shared-file-rules.test.ts +238 -0
- package/src/modules/agent-instructions/agent-instructions.routes.ts +12 -2
- package/src/modules/agent-instructions/compose.ts +50 -7
- package/src/modules/agent-instructions/index.ts +12 -0
- package/src/modules/agent-instructions/shared-file-rules.ts +314 -0
- package/src/modules/audit/agent-audit.service.ts +4 -2
- package/src/modules/auth/__tests__/account-deactivation.test.ts +2 -1
- package/src/modules/auth/__tests__/account-erasure.approval-gate.test.ts +6 -2
- package/src/modules/auth/__tests__/account.routes.test.ts +6 -3
- package/src/modules/auth/__tests__/auth.service.test.ts +3 -1
- package/src/modules/auth/account-erasure.service.ts +69 -18
- package/src/modules/auth/auth.service.ts +33 -23
- package/src/modules/code-mode/__tests__/chain-runtime.e2e.test.ts +335 -0
- package/src/modules/code-mode/__tests__/code-mode.tool.test.ts +46 -2
- package/src/modules/code-mode/code-mode.tool.ts +80 -34
- package/src/modules/database/__tests__/connection.test.ts +12 -0
- package/src/modules/database/__tests__/pii-backfill.pg.test.ts +780 -0
- package/src/modules/database/connection.ts +117 -0
- package/src/modules/database/core-schema.ts +81 -31
- package/src/modules/database/migrate.ts +540 -2
- package/src/modules/kb-fs/__tests__/locking-filesystem.test.ts +93 -0
- package/src/modules/kb-fs/locking-filesystem.ts +37 -0
- package/src/modules/mcp/__tests__/mcp.e2e.test.ts +4 -3
- package/src/modules/mcp/__tests__/mcp.service.test.ts +47 -7
- package/src/modules/mcp/mcp.service.ts +46 -7
- package/src/modules/plugins/join-request-records.store.ts +7 -4
- package/src/modules/tool-auth/external-api-key.service.ts +8 -2
- package/src/modules/tool-manuals/__tests__/tool-manuals.service.test.ts +151 -0
- package/src/modules/tool-manuals/tool-manuals.service.ts +37 -0
- package/src/modules/tool-manuals/tool-manuals.tools.ts +44 -31
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +378 -0
- package/src/modules/tool-registry/description-length.ts +111 -0
- package/src/modules/workflow/git/__tests__/git.service.prFetchFailure.test.ts +170 -0
- package/src/modules/workflow/git/git.service.ts +47 -1
- package/src/modules/workflow/pending-commits.service.ts +5 -1
- package/src/modules/workflow/recovery-bot.ts +7 -3
- package/src/modules/workflow/review-workflow/__tests__/carry-approvals-forward.test.ts +4 -0
- package/src/modules/workflow/review-workflow/__tests__/erase-approver.test.ts +25 -3
- package/src/modules/workflow/review-workflow/review-workflow.service.ts +12 -3
- package/src/modules/workflow/workflow.service.ts +4 -1
- package/src/modules/workspace/__tests__/agent-uploads.test.ts +1604 -0
- package/src/modules/workspace/__tests__/escape-sequences.routes.test.ts +24 -14
- package/src/modules/workspace/__tests__/workspace.service.any-workspace-credentials.test.ts +156 -0
- package/src/modules/workspace/__tests__/workspace.service.replaced-repository.test.ts +77 -1
- package/src/modules/workspace/__tests__/workspace.service.test.ts +57 -0
- package/src/modules/workspace/__tests__/workspace.tools.agents-file.test.ts +39 -36
- package/src/modules/workspace/__tests__/workspace.tools.test.ts +196 -46
- package/src/modules/workspace/agent-upload.routes.ts +214 -0
- package/src/modules/workspace/agent-upload.store.ts +668 -0
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +9 -9
- package/src/modules/workspace/startup/steps/seed-tree.ts +3 -6
- package/src/modules/workspace/startup/steps/template-source.ts +53 -5
- package/src/modules/workspace/upload-limits.ts +12 -0
- package/src/modules/workspace/workspace.routes.ts +1 -2
- package/src/modules/workspace/workspace.service.ts +63 -37
- package/src/modules/workspace/workspace.tools.ts +647 -148
- package/src/modules/workspace/write-denial.ts +0 -8
- package/src/modules/workspace/zip-entry-rules.ts +173 -0
- package/src/shared/__tests__/column-crypto.test.ts +217 -0
- package/src/shared/column-crypto.ts +218 -0
- package/src/shared/token-crypto.ts +28 -1
- package/src/tenancy/__tests__/static-tenant-source.test.ts +4 -0
- package/src/tenancy/static-tenant-source.ts +1 -0
- package/src/tenancy/tenant-secrets.ts +5 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { migrate } from 'drizzle-orm/node-postgres/migrator';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
2
3
|
import { logger } from '../../shared/logging.js';
|
|
3
4
|
const log = logger('database');
|
|
4
|
-
import { DEFAULT_DB_SCHEMA, dbSchemaOf } from './connection.js';
|
|
5
|
+
import { DEFAULT_DB_SCHEMA, dbSchemaOf, piiKeysOf } from './connection.js';
|
|
5
6
|
import { AdvisoryLock, withAdvisoryLock } from './advisory-lock.js';
|
|
7
|
+
import { PII_SEALED_SHAPE_SQL_REGEX, isEncryptedBlob } from '../../shared/column-crypto.js';
|
|
6
8
|
/*
|
|
7
9
|
* ── Per-tier migration folders ──────────────────────────────────────────────
|
|
8
10
|
*
|
|
@@ -62,14 +64,24 @@ export async function runCoreMigrations(db, folder) {
|
|
|
62
64
|
log.info('Running core database migrations...');
|
|
63
65
|
await migrate(db, { migrationsFolder: folder, migrationsTable: '__drizzle_migrations_core', migrationsSchema });
|
|
64
66
|
log.info('Core migrations complete.');
|
|
67
|
+
// Under the same lock, before anything reads or writes a PII column:
|
|
68
|
+
// the data half of migration 0016, which SQL cannot do (see below).
|
|
69
|
+
await runPiiEncryptionBackfill(db);
|
|
65
70
|
}, { tenantKey });
|
|
66
71
|
}
|
|
67
72
|
/**
|
|
68
73
|
* Apply the ENTERPRISE migration history from `folder`, tracked in
|
|
69
74
|
* `__drizzle_migrations_enterprise`. Run AFTER {@link runCoreMigrations} —
|
|
70
75
|
* enterprise tables FK into core tables.
|
|
76
|
+
*
|
|
77
|
+
* `piiBackfill` is the overlay's own personal data, for the overlay that has
|
|
78
|
+
* sealed columns of its own (`encryptedText` / `blindIndexText` in its
|
|
79
|
+
* schema): the rows written before those columns were sealed are sealed here,
|
|
80
|
+
* right after the history that adds the index columns and under the same
|
|
81
|
+
* lock, exactly as {@link runCoreMigrations} does for core's tables. See
|
|
82
|
+
* {@link runPiiBackfill}.
|
|
71
83
|
*/
|
|
72
|
-
export async function runEnterpriseMigrations(db, folder) {
|
|
84
|
+
export async function runEnterpriseMigrations(db, folder, opts = {}) {
|
|
73
85
|
const { migrationsSchema, tenantKey } = ledgerOptions(db);
|
|
74
86
|
await withAdvisoryLock(db, AdvisoryLock.Migrations, async () => {
|
|
75
87
|
log.info('Running enterprise database migrations...');
|
|
@@ -79,6 +91,382 @@ export async function runEnterpriseMigrations(db, folder) {
|
|
|
79
91
|
migrationsSchema,
|
|
80
92
|
});
|
|
81
93
|
log.info('Enterprise migrations complete.');
|
|
94
|
+
if (opts.piiBackfill)
|
|
95
|
+
await runPiiBackfill(db, opts.piiBackfill);
|
|
82
96
|
}, { tenantKey });
|
|
83
97
|
}
|
|
98
|
+
const PII_BACKFILL_TABLES = [
|
|
99
|
+
{ table: 'users', key: ['id'], encrypted: ['email', 'name', 'avatar_url'], bidx: { source: 'email', column: 'email_bidx' } },
|
|
100
|
+
{ table: 'pr_file_approvals', key: ['id'], encrypted: ['approver_email', 'approver_name'], bidx: { source: 'approver_email', column: 'approver_email_bidx' } },
|
|
101
|
+
{ table: 'pr_merge_log', key: ['id'], encrypted: ['triggered_by_email', 'triggered_by_name', 'error'], bidx: { source: 'triggered_by_email', column: 'triggered_by_email_bidx' } },
|
|
102
|
+
{ table: 'pr_comments', key: ['id'], encrypted: ['author_email', 'author_name', 'body'], bidx: { source: 'author_email', column: 'author_email_bidx' } },
|
|
103
|
+
{ table: 'change_requests', key: ['id'], encrypted: ['author_email', 'author_name', 'title', 'body', 'apply_failure_reason', 'apply_failed_by_name'], bidx: { source: 'author_email', column: 'author_email_bidx' } },
|
|
104
|
+
{ table: 'pending_commits', key: ['id'], encrypted: ['author_email', 'author_name', 'last_error'], bidx: { source: 'author_email', column: 'author_email_bidx' } },
|
|
105
|
+
{ table: 'file_locks', key: ['workspace_id', 'branch', 'path'], encrypted: ['holder_name'] },
|
|
106
|
+
{ table: 'plugin_join_requests', key: ['id'], encrypted: ['requester_email', 'requester_name', 'failure_reason'], bidx: { source: 'requester_email', column: 'requester_email_bidx' } },
|
|
107
|
+
];
|
|
108
|
+
const ident = (name) => sql.raw(`"${name}"`);
|
|
109
|
+
/** A table's blind indexes, however many it declares. */
|
|
110
|
+
const indexesOf = (t) => (t.bidx === undefined ? [] : Array.isArray(t.bidx) ? t.bidx : [t.bidx]);
|
|
111
|
+
/** The name the refusals call the key by: the operator's own, with core's tenant note when it is core's. */
|
|
112
|
+
const keyNameOf = (spec) => spec.keyName ?? 'SECRETS_ENC_KEY (for a tenant: the one derived from TENANT_MASTER_KEY)';
|
|
113
|
+
/** A text column as it is stored: bytes, so the handle's connection does not open it. */
|
|
114
|
+
const asStored = (col) => sql `convert_to(${ident(col)}, 'UTF8') AS ${ident(col)}`;
|
|
115
|
+
/** What {@link asStored} selected, back as text; anything else as it came. */
|
|
116
|
+
const storedText = (value) => (Buffer.isBuffer(value) ? value.toString('utf8') : value);
|
|
117
|
+
/**
|
|
118
|
+
* A column "needs sealing" when it holds non-empty text that isn't a blob.
|
|
119
|
+
* The shape regex is deliberately used as a NEGATIVE filter: anything failing
|
|
120
|
+
* it — including plaintext that merely BEGINS with the prefix — is selected
|
|
121
|
+
* and sealed, and `isEncryptedBlob` is the same pattern, so the two can never
|
|
122
|
+
* disagree about a value. Sealed rows all match, keeping the steady-state scan
|
|
123
|
+
* an empty-result query.
|
|
124
|
+
*
|
|
125
|
+
* `trustShape` is false on the FIRST backfill of a database (see
|
|
126
|
+
* {@link isFirstBackfill}): nothing has been sealed yet, so a value in this
|
|
127
|
+
* shape is plaintext somebody typed, and every non-empty value is selected.
|
|
128
|
+
*/
|
|
129
|
+
function needsSealing(col, trustShape) {
|
|
130
|
+
if (!trustShape)
|
|
131
|
+
return sql `(${ident(col)} IS NOT NULL AND ${ident(col)} <> '')`;
|
|
132
|
+
return sql `(${ident(col)} IS NOT NULL AND ${ident(col)} <> '' AND ${ident(col)} !~ ${PII_SEALED_SHAPE_SQL_REGEX})`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Whether this database has never been through the backfill: the spec's
|
|
136
|
+
* marker column (for core, the blind index of `users`) is still nullable.
|
|
137
|
+
* The backfill, the duplicate collapse and the constraints run in ONE
|
|
138
|
+
* transaction, so a nullable column means no backfill has ever committed
|
|
139
|
+
* here — and therefore that nothing in it is sealed, whatever it looks like.
|
|
140
|
+
*
|
|
141
|
+
* That is what lets a first backfill trust no shape at all. A blob is
|
|
142
|
+
* recognised by its shape, and a title or a display name is text a person
|
|
143
|
+
* chose: one typed in that shape before the upgrade would be read as sealed,
|
|
144
|
+
* skipped, and left in clear for good — and, sampled by
|
|
145
|
+
* {@link assertKeyOpensSealedRows}, would refuse the start for a key that is
|
|
146
|
+
* perfectly right. Once the first backfill has committed, every write goes
|
|
147
|
+
* through a handle that seals it, so no such value can arrive again.
|
|
148
|
+
*
|
|
149
|
+
* A marker that is not there at all is the SQL history not having run (or a
|
|
150
|
+
* spec naming a column it does not add): refused, rather than read as "not
|
|
151
|
+
* the first run", which would trust every shape in a database nobody sealed.
|
|
152
|
+
*/
|
|
153
|
+
async function isFirstBackfill(tx, spec) {
|
|
154
|
+
const { table, column } = spec.marker;
|
|
155
|
+
const result = await tx.execute(sql `
|
|
156
|
+
SELECT is_nullable FROM information_schema.columns
|
|
157
|
+
WHERE table_schema = current_schema() AND table_name = ${table} AND column_name = ${column}
|
|
158
|
+
`);
|
|
159
|
+
const nullable = result.rows[0]?.is_nullable;
|
|
160
|
+
if (nullable === undefined) {
|
|
161
|
+
throw new Error(`PII encryption backfill (${spec.name}): the marker column ${table}.${column} does not exist. ` +
|
|
162
|
+
'Apply the migration that adds the blind-index columns before the backfill runs.');
|
|
163
|
+
}
|
|
164
|
+
return nullable === 'YES';
|
|
165
|
+
}
|
|
166
|
+
/** How many rows one backfill query reads: a table is walked in batches, never loaded whole. */
|
|
167
|
+
const BACKFILL_BATCH_ROWS = 500;
|
|
168
|
+
/**
|
|
169
|
+
* The configured key must open what earlier starts sealed. A rotated or
|
|
170
|
+
* mistyped `SECRETS_ENC_KEY` would otherwise go unnoticed here — sealed rows
|
|
171
|
+
* are exactly the ones the scan skips — and surface only as every login
|
|
172
|
+
* failing and every lookup by email missing, because the blind indexes would
|
|
173
|
+
* be computed under the new key. One sealed value per table is enough: all
|
|
174
|
+
* rows of a deployment are sealed under one key, so any one of them answers
|
|
175
|
+
* for the rest. Refusing the start is the loud failure; re-keying a database
|
|
176
|
+
* is a deliberate operation, not a boot.
|
|
177
|
+
*
|
|
178
|
+
* The sample is a sealed value from WHICHEVER column has one, not from a
|
|
179
|
+
* column chosen to stand for its table. A column may hold nothing in any
|
|
180
|
+
* row (an optional address nobody gave, an error text that never occurred),
|
|
181
|
+
* and a check that looked only there would find no sample, say nothing, and
|
|
182
|
+
* let a wrong key through to a table whose other columns are full of values
|
|
183
|
+
* it cannot open.
|
|
184
|
+
*/
|
|
185
|
+
async function assertKeyOpensSealedRows(tx, keys, spec) {
|
|
186
|
+
for (const t of spec.tables) {
|
|
187
|
+
// ONE pass over the table, which ends at the first row that has a sealed
|
|
188
|
+
// value in ANY of its sealed columns: on a sealed database, the first
|
|
189
|
+
// row. Whatever that row holds sealed is tried. (A value in it that is
|
|
190
|
+
// not sealed opens as itself, so it is no evidence either way.)
|
|
191
|
+
const sealedSomewhere = sql.join(t.encrypted.map((col) => sql `${ident(col)} ~ ${PII_SEALED_SHAPE_SQL_REGEX}`), sql ` OR `);
|
|
192
|
+
const sample = await tx.execute(sql `SELECT ${sql.join(t.encrypted.map(asStored), sql `, `)} FROM ${ident(t.table)} WHERE ${sealedSomewhere} LIMIT 1`);
|
|
193
|
+
const row = (sample.rows[0] ?? {});
|
|
194
|
+
for (const col of t.encrypted) {
|
|
195
|
+
const value = storedText(row[col]);
|
|
196
|
+
if (typeof value === 'string' && !keys.open(value).ok) {
|
|
197
|
+
throw new Error(`PII encryption: ${t.table}.${col} is sealed with a key the configured ${keyNameOf(spec)} ` +
|
|
198
|
+
'does not open — refusing to start. ' +
|
|
199
|
+
'Restore the key that sealed it; changing the key is a re-keying of the database, not a configuration change.');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function backfillTable(tx, keys, t, trustShape, keyName) {
|
|
205
|
+
const indexes = indexesOf(t);
|
|
206
|
+
// The key columns are read as text beside themselves, so the next batch can
|
|
207
|
+
// be asked for by value whatever their type is (a uuid, a serial, a path).
|
|
208
|
+
const keyText = (k) => `${k}__key`;
|
|
209
|
+
const cols = [
|
|
210
|
+
...t.key.map(ident),
|
|
211
|
+
...t.key.map((k) => sql `${ident(k)}::text AS ${ident(keyText(k))}`),
|
|
212
|
+
...t.encrypted.map(asStored),
|
|
213
|
+
...indexes.map((index) => ident(index.column)),
|
|
214
|
+
];
|
|
215
|
+
// Only rows with work left: the ciphertext prefix makes "unsealed" a plain
|
|
216
|
+
// SQL predicate, so a fully-sealed table costs one empty-result query.
|
|
217
|
+
// AN INDEX IS THERE EXACTLY WHERE ITS SOURCE IS. A row is owed work when
|
|
218
|
+
// the two disagree, either way: a source with no index beside it, and an
|
|
219
|
+
// index left beside a source that has since been emptied to NULL (which
|
|
220
|
+
// would go on answering for an address the row no longer holds). A source
|
|
221
|
+
// that is NULL with no index is how an optional address looks, and is not
|
|
222
|
+
// asked for: it would come back on every start for nothing.
|
|
223
|
+
const pending = [
|
|
224
|
+
...t.encrypted.map((col) => needsSealing(col, trustShape)),
|
|
225
|
+
...indexes.map((index) => sql `((${ident(index.column)} IS NULL) <> (${ident(index.source)} IS NULL))`),
|
|
226
|
+
];
|
|
227
|
+
const keyTuple = sql `(${sql.join(t.key.map((k) => sql `${ident(k)}::text`), sql `, `)})`;
|
|
228
|
+
// A blob, as far as this pass may believe one. On a first backfill nothing
|
|
229
|
+
// is sealed yet, so nothing is believed (see `isFirstBackfill`).
|
|
230
|
+
const sealedAlready = (value) => trustShape && isEncryptedBlob(value);
|
|
231
|
+
let rewritten = 0;
|
|
232
|
+
/** The key of the last row read, as text: the next batch starts after it. */
|
|
233
|
+
let after = null;
|
|
234
|
+
// IN BATCHES, in key order. A table is never loaded whole: a deployment with
|
|
235
|
+
// years of change requests holds every title and body it ever had, and
|
|
236
|
+
// reading them into one result held the first start after the upgrade — and
|
|
237
|
+
// the memory of the process making it — for as long as that took. Walking by
|
|
238
|
+
// key rather than re-asking for "what is left" also ends: a row this pass
|
|
239
|
+
// leaves as it found it (a write overtook it) is passed, not met again.
|
|
240
|
+
for (;;) {
|
|
241
|
+
const past = after ? sql ` AND ${keyTuple} > (${sql.join(after.map((v) => sql `${v}`), sql `, `)})` : sql ``;
|
|
242
|
+
const result = await tx.execute(sql `SELECT ${sql.join(cols, sql `, `)} FROM ${ident(t.table)} WHERE (${sql.join(pending, sql ` OR `)})${past} ORDER BY ${keyTuple} LIMIT ${BACKFILL_BATCH_ROWS}`);
|
|
243
|
+
const batch = result.rows;
|
|
244
|
+
for (const read of batch) {
|
|
245
|
+
const row = Object.fromEntries(Object.entries(read).map(([col, value]) => [col, storedText(value)]));
|
|
246
|
+
const sets = [];
|
|
247
|
+
// Compare-and-swap: pin every value this row was read with, so a write
|
|
248
|
+
// that lands between scan and update makes this UPDATE match zero rows
|
|
249
|
+
// instead of clobbering the newer value.
|
|
250
|
+
const where = t.key.map((k) => sql `${ident(k)} = ${row[k]}`);
|
|
251
|
+
for (const col of t.encrypted) {
|
|
252
|
+
const value = row[col];
|
|
253
|
+
if (typeof value === 'string' && value !== '' && !sealedAlready(value)) {
|
|
254
|
+
sets.push(sql `${ident(col)} = ${keys.seal(value)}`);
|
|
255
|
+
where.push(sql `${ident(col)} = ${value}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
for (const index of indexes) {
|
|
259
|
+
// The blind index is derived from its source, so it is (re)computed
|
|
260
|
+
// whenever the source is being sealed in this pass — a legacy writer
|
|
261
|
+
// that put a NEW plaintext email on an already-indexed row left a stale
|
|
262
|
+
// index behind, and sealing the email alone would freeze that mismatch
|
|
263
|
+
// — and whenever it was never filled. The source may already be
|
|
264
|
+
// ciphertext (an index that was never filled beside a sealed address);
|
|
265
|
+
// the index is always computed over the plaintext, never over a blob
|
|
266
|
+
// the key cannot open.
|
|
267
|
+
const source = row[index.source];
|
|
268
|
+
const stored = row[index.column];
|
|
269
|
+
// NULL is "no value", and has no index: not the index of the empty
|
|
270
|
+
// string, which every row without an address would then share where
|
|
271
|
+
// a plain unique index let any number of NULLs stand. An index found
|
|
272
|
+
// beside a NULL source is taken away, pinned to what was read like
|
|
273
|
+
// every other write here.
|
|
274
|
+
//
|
|
275
|
+
// The EMPTY STRING is a value, and keeps its index: it is what the
|
|
276
|
+
// handle writes for one (`blindIndexText`), so a row is found by it
|
|
277
|
+
// whether the application wrote it or this did, and two empty
|
|
278
|
+
// strings are equal under a unique index, as they were in clear.
|
|
279
|
+
if (typeof source !== 'string') {
|
|
280
|
+
if (stored != null) {
|
|
281
|
+
sets.push(sql `${ident(index.column)} = NULL`);
|
|
282
|
+
where.push(sql `${ident(index.column)} = ${stored}`);
|
|
283
|
+
where.push(sql `${ident(index.source)} IS NULL`);
|
|
284
|
+
}
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const text = source;
|
|
288
|
+
const sealingSource = text !== '' && !sealedAlready(text);
|
|
289
|
+
if (sealingSource || stored == null) {
|
|
290
|
+
const opened = sealedAlready(text) ? keys.open(text) : { ok: true, plain: text };
|
|
291
|
+
if (!opened.ok) {
|
|
292
|
+
throw new Error(`PII encryption backfill: ${t.table}.${index.source} cannot be decrypted with the ` +
|
|
293
|
+
`configured ${keyName} — refusing to derive a blind index from ciphertext. ` +
|
|
294
|
+
'Restore the key that sealed it, then restart.');
|
|
295
|
+
}
|
|
296
|
+
sets.push(sql `${ident(index.column)} = ${keys.index(opened.plain)}`);
|
|
297
|
+
// Pin the index AND its source: if a concurrent writer replaces the
|
|
298
|
+
// email between scan and write, the CAS must not attach the OLD
|
|
299
|
+
// email's blind index to the NEW value.
|
|
300
|
+
where.push(sql `${ident(index.column)} IS NOT DISTINCT FROM ${stored ?? null}`);
|
|
301
|
+
where.push(sql `${ident(index.source)} IS NOT DISTINCT FROM ${source ?? null}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (sets.length === 0)
|
|
305
|
+
continue;
|
|
306
|
+
const updated = await tx.execute(sql `UPDATE ${ident(t.table)} SET ${sql.join(sets, sql `, `)} WHERE ${sql.join(where, sql ` AND `)}`);
|
|
307
|
+
rewritten += updated.rowCount ?? 0;
|
|
308
|
+
}
|
|
309
|
+
if (batch.length < BACKFILL_BATCH_ROWS)
|
|
310
|
+
return rewritten;
|
|
311
|
+
const last = batch[batch.length - 1];
|
|
312
|
+
after = t.key.map((k) => String(last[keyText(k)]));
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* The name on a refused apply that was recorded BEFORE this release: the
|
|
317
|
+
* address of whoever it names was never kept, so no blind index stands beside
|
|
318
|
+
* it and an account erasure has nothing to find it by.
|
|
319
|
+
*
|
|
320
|
+
* Matching it by display name instead got both mistakes at once: a person
|
|
321
|
+
* who had since changed their name kept the old one on the request for good,
|
|
322
|
+
* and somebody else of the same name had theirs taken away. So the name goes,
|
|
323
|
+
* once, here. The refusal itself stays, with its reason and its time; it
|
|
324
|
+
* reads "could not apply" without saying who. A refusal recorded from this
|
|
325
|
+
* release on carries its index and is never touched by this.
|
|
326
|
+
*/
|
|
327
|
+
async function clearUnindexedRefusalNames(tx) {
|
|
328
|
+
const cleared = await tx.execute(sql.raw(`
|
|
329
|
+
UPDATE "change_requests" SET "apply_failed_by_name" = NULL
|
|
330
|
+
WHERE "apply_failed_by_name" IS NOT NULL AND "apply_failed_by_email_bidx" IS NULL
|
|
331
|
+
`));
|
|
332
|
+
return cleared.rowCount ?? 0;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Legacy uniqueness was on the RAW email, so rows differing only by case or
|
|
336
|
+
* whitespace could coexist; under the normalized blind index they collide and
|
|
337
|
+
* the unique-index creation below would abort the start. Approval rows and
|
|
338
|
+
* join-request rows are the same logical row — collapse them, keeping the
|
|
339
|
+
* earliest. Colliding USER rows are distinct accounts; refuse loudly with the
|
|
340
|
+
* ids so an operator resolves the conflict deliberately instead of the
|
|
341
|
+
* upgrade guessing.
|
|
342
|
+
*/
|
|
343
|
+
async function resolveBidxCollisions(tx) {
|
|
344
|
+
await tx.execute(sql.raw(`
|
|
345
|
+
DELETE FROM "pr_file_approvals" a USING "pr_file_approvals" b
|
|
346
|
+
WHERE a."pr_number" = b."pr_number" AND a."path" = b."path"
|
|
347
|
+
AND a."approver_email_bidx" = b."approver_email_bidx" AND a."head_sha" = b."head_sha"
|
|
348
|
+
AND (a."approved_at" > b."approved_at" OR (a."approved_at" = b."approved_at" AND a."id" > b."id"))
|
|
349
|
+
`));
|
|
350
|
+
await tx.execute(sql.raw(`
|
|
351
|
+
DELETE FROM "plugin_join_requests" a USING "plugin_join_requests" b
|
|
352
|
+
WHERE a."plugin_key" = b."plugin_key" AND a."requester_email_bidx" = b."requester_email_bidx"
|
|
353
|
+
AND (a."created_at" > b."created_at" OR (a."created_at" = b."created_at" AND a."id" > b."id"))
|
|
354
|
+
`));
|
|
355
|
+
const dupes = await tx.execute(sql.raw(`
|
|
356
|
+
SELECT array_agg("id") AS ids FROM "users" GROUP BY "email_bidx" HAVING count(*) > 1
|
|
357
|
+
`));
|
|
358
|
+
if (dupes.rows.length > 0) {
|
|
359
|
+
const groups = dupes.rows.map((r) => r.ids.join(', '));
|
|
360
|
+
throw new Error('PII encryption backfill: multiple user rows share the same email after normalization ' +
|
|
361
|
+
'(case/whitespace variants of one address). Merge or delete the duplicates, then restart. ' +
|
|
362
|
+
`Conflicting user ids: [${groups.join('], [')}]`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const PII_FINALIZE_STATEMENTS = [
|
|
366
|
+
'ALTER TABLE "users" ALTER COLUMN "email_bidx" SET NOT NULL',
|
|
367
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS "users_email_bidx_unq" ON "users" ("email_bidx")',
|
|
368
|
+
'ALTER TABLE "users" DROP CONSTRAINT IF EXISTS "users_email_unique"',
|
|
369
|
+
'ALTER TABLE "pr_file_approvals" ALTER COLUMN "approver_email_bidx" SET NOT NULL',
|
|
370
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS "pr_file_approvals_bidx_unq" ON "pr_file_approvals" ("pr_number","path","approver_email_bidx","head_sha")',
|
|
371
|
+
'DROP INDEX IF EXISTS "pr_file_approvals_unq"',
|
|
372
|
+
'ALTER TABLE "pr_merge_log" ALTER COLUMN "triggered_by_email_bidx" SET NOT NULL',
|
|
373
|
+
'ALTER TABLE "pr_comments" ALTER COLUMN "author_email_bidx" SET NOT NULL',
|
|
374
|
+
'ALTER TABLE "change_requests" ALTER COLUMN "author_email_bidx" SET NOT NULL',
|
|
375
|
+
'ALTER TABLE "pending_commits" ALTER COLUMN "author_email_bidx" SET NOT NULL',
|
|
376
|
+
'ALTER TABLE "plugin_join_requests" ALTER COLUMN "requester_email_bidx" SET NOT NULL',
|
|
377
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS "plugin_join_requests_requester_bidx_plugin_unq" ON "plugin_join_requests" ("requester_email_bidx","plugin_key")',
|
|
378
|
+
'DROP INDEX IF EXISTS "plugin_join_requests_requester_plugin_unq"',
|
|
379
|
+
];
|
|
380
|
+
/** Core's own personal data: the tables above, and what migration 0016 deferred. */
|
|
381
|
+
const CORE_PII_BACKFILL = {
|
|
382
|
+
name: 'core',
|
|
383
|
+
tables: PII_BACKFILL_TABLES,
|
|
384
|
+
marker: { table: 'users', column: 'email_bidx' },
|
|
385
|
+
afterSealing: async (tx, { first }) => {
|
|
386
|
+
const cleared = await clearUnindexedRefusalNames(tx);
|
|
387
|
+
if (cleared > 0) {
|
|
388
|
+
log.info(`PII encryption backfill: took the name off ${cleared} refused apply(ies) recorded before this release.`);
|
|
389
|
+
}
|
|
390
|
+
// Only where the unique indexes are not up yet. Once they are, they are
|
|
391
|
+
// what keeps two rows from sharing an index, so there is nothing to
|
|
392
|
+
// collapse — and the collapse is two self-joins and an aggregate over
|
|
393
|
+
// whole tables, which every later start paid for nothing.
|
|
394
|
+
if (first)
|
|
395
|
+
await resolveBidxCollisions(tx);
|
|
396
|
+
},
|
|
397
|
+
finalize: PII_FINALIZE_STATEMENTS,
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* Encrypt pre-existing plaintext PII rows, fill the blind-index columns, and
|
|
401
|
+
* apply the constraints migration 0016 deferred. Idempotent; `runCoreMigrations`
|
|
402
|
+
* runs it under the migrations lock right after the history. The handle must
|
|
403
|
+
* hold the knowledge base's key (`createDb(url, { piiKey })`; the composition
|
|
404
|
+
* root's does): one that holds none is refused before anything is read.
|
|
405
|
+
*/
|
|
406
|
+
export async function runPiiEncryptionBackfill(db) {
|
|
407
|
+
await runPiiBackfill(db, CORE_PII_BACKFILL);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Seal the rows `spec` names that were written before their columns were
|
|
411
|
+
* sealed, fill their blind indexes, and apply `spec.finalize` — the DATA half
|
|
412
|
+
* of a migration that adds `encryptedText` / `blindIndexText` columns, which
|
|
413
|
+
* SQL cannot do because it holds no key. ONE implementation for every schema
|
|
414
|
+
* on a handle: core's own tables ({@link runPiiEncryptionBackfill}) and an
|
|
415
|
+
* overlay's, so the rules above (one transaction, a first run that trusts no
|
|
416
|
+
* shape, a key that must open what is sealed, columns read as stored, writes
|
|
417
|
+
* that pin what they read) are not rewritten per schema and cannot drift.
|
|
418
|
+
*
|
|
419
|
+
* Idempotent, and to be run on every start under the lock that guards the
|
|
420
|
+
* schema's history: {@link runEnterpriseMigrations} does that for an overlay
|
|
421
|
+
* that passes its spec. The handle must hold the key the rows are sealed
|
|
422
|
+
* with; one that holds none is refused before anything is read.
|
|
423
|
+
*/
|
|
424
|
+
export async function runPiiBackfill(db, spec) {
|
|
425
|
+
const keys = piiKeysOf(db);
|
|
426
|
+
assertSpecIsWhole(spec);
|
|
427
|
+
const label = spec.name === CORE_PII_BACKFILL.name ? 'PII encryption backfill' : `PII encryption backfill (${spec.name})`;
|
|
428
|
+
await db.transaction(async (tx) => {
|
|
429
|
+
const first = await isFirstBackfill(tx, spec);
|
|
430
|
+
// Nothing is sealed before the first backfill, so there is no sealed row
|
|
431
|
+
// for the key to be checked against — and a value that only LOOKS sealed
|
|
432
|
+
// must not be taken for one (see `isFirstBackfill`).
|
|
433
|
+
if (!first)
|
|
434
|
+
await assertKeyOpensSealedRows(tx, keys, spec);
|
|
435
|
+
let rewritten = 0;
|
|
436
|
+
for (const t of spec.tables) {
|
|
437
|
+
rewritten += await backfillTable(tx, keys, t, !first, keyNameOf(spec));
|
|
438
|
+
}
|
|
439
|
+
if (rewritten > 0)
|
|
440
|
+
log.info(`${label}: rewrote ${rewritten} row(s).`);
|
|
441
|
+
await spec.afterSealing?.(tx, { first });
|
|
442
|
+
for (const statement of spec.finalize) {
|
|
443
|
+
await tx.execute(sql.raw(statement));
|
|
444
|
+
}
|
|
445
|
+
// The marker is what the NEXT start reads. Left nullable, that start
|
|
446
|
+
// would be a "first" one again: it would believe no shape, take every
|
|
447
|
+
// sealed value for plaintext and seal it a second time, and nothing
|
|
448
|
+
// would open afterwards. So a spec that does not close its own marker
|
|
449
|
+
// does not commit.
|
|
450
|
+
if (await isFirstBackfill(tx, spec)) {
|
|
451
|
+
throw new Error(`${label}: finalize left the marker column ${spec.marker.table}.${spec.marker.column} nullable. ` +
|
|
452
|
+
'It must set it NOT NULL, or the next start would seal the sealed rows again. Nothing was changed.');
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
/** A spec is refused for what would otherwise fail halfway: a marker that is no index of its tables, an index of a column that is not sealed. */
|
|
457
|
+
function assertSpecIsWhole(spec) {
|
|
458
|
+
let markerIsAnIndex = false;
|
|
459
|
+
for (const t of spec.tables) {
|
|
460
|
+
for (const index of indexesOf(t)) {
|
|
461
|
+
if (!t.encrypted.includes(index.source)) {
|
|
462
|
+
throw new Error(`PII encryption backfill (${spec.name}): ${t.table}.${index.column} indexes "${index.source}", which is not one of the table's encrypted columns.`);
|
|
463
|
+
}
|
|
464
|
+
if (t.table === spec.marker.table && index.column === spec.marker.column)
|
|
465
|
+
markerIsAnIndex = true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (!markerIsAnIndex) {
|
|
469
|
+
throw new Error(`PII encryption backfill (${spec.name}): the marker ${spec.marker.table}.${spec.marker.column} is not a blind-index column of the spec's tables.`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
84
472
|
//# sourceMappingURL=migrate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/modules/database/migrate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAiB,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH;;;;;;;;;;;;;GAaG;AACH,SAAS,aAAa,CAAC,EAAY;IACjC,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;IAC9B,OAAO,MAAM,KAAK,iBAAiB;QACjC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;QACnB,CAAC,CAAC,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,EAAY,EAAE,MAAc;IAClE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,gBAAgB,CACpB,EAAE,EACF,YAAY,CAAC,UAAU,EACvB,KAAK,IAAI,EAAE;QACT,GAAG,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAChD,MAAM,OAAO,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAChH,GAAG,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACxC,CAAC,EACD,EAAE,SAAS,EAAE,CACd,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAAY,EAAE,MAAc;IACxE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC,CAAC;IAC1D,MAAM,gBAAgB,CACpB,EAAE,EACF,YAAY,CAAC,UAAU,EACvB,KAAK,IAAI,EAAE;QACT,GAAG,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QACtD,MAAM,OAAO,CAAC,EAAE,EAAE;YAChB,gBAAgB,EAAE,MAAM;YACxB,eAAe,EAAE,iCAAiC;YAClD,gBAAgB;SACjB,CAAC,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IAC9C,CAAC,EACD,EAAE,SAAS,EAAE,CACd,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/modules/database/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -114,6 +114,23 @@ export declare class LockingFilesystem extends GitGuardedFilesystem {
|
|
|
114
114
|
* the write and releases the lock untouched; the caller gets what it threw.
|
|
115
115
|
*/
|
|
116
116
|
writeFile(inputPath: string, content: FileContent, options?: WriteOptions, check?: () => void | Promise<void>): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Write a file whose new content DEPENDS on what it holds: the read, the
|
|
119
|
+
* computing of the new bytes and the write all happen with the path's lock
|
|
120
|
+
* held, so what lands is derived from the very bytes it replaces.
|
|
121
|
+
*
|
|
122
|
+
* `writeFile` cannot give that. Its content is fixed before the lock is
|
|
123
|
+
* taken, so a caller that read the file first is writing over a state
|
|
124
|
+
* anyone may have moved in between: an `edit_file` replacing an empty owner
|
|
125
|
+
* field with one name would silently overwrite another name written a
|
|
126
|
+
* moment earlier, and both callers would be told their edit landed.
|
|
127
|
+
*
|
|
128
|
+
* `rewrite` receives the file as it is under the lock (null when nothing is
|
|
129
|
+
* there) and answers the bytes to write. Throwing refuses: nothing is
|
|
130
|
+
* written, the lock is released untouched, and the caller gets what was
|
|
131
|
+
* thrown.
|
|
132
|
+
*/
|
|
133
|
+
rewriteFile(inputPath: string, rewrite: (current: Buffer | null) => FileContent | Promise<FileContent>): Promise<void>;
|
|
117
134
|
appendFile(inputPath: string, content: FileContent): Promise<void>;
|
|
118
135
|
deleteFile(inputPath: string, options?: RemoveOptions): Promise<void>;
|
|
119
136
|
copyFile(src: string, dest: string, options?: CopyOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locking-filesystem.d.ts","sourceRoot":"","sources":["../../../src/modules/kb-fs/locking-filesystem.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAOH,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAE1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAIpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAqB,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAgBpF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG;IAC5F,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACvC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAED,qBAAa,iBAAkB,SAAQ,oBAAoB;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA2B;gBAGrD,OAAO,EAAE,sBAAsB,EAC/B,WAAW,EAAE,wBAAwB;IAMvC;;;;;;;;;OASG;IACY,SAAS,CACtB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,YAAY,EACtB,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"locking-filesystem.d.ts","sourceRoot":"","sources":["../../../src/modules/kb-fs/locking-filesystem.ts"],"names":[],"mappings":"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"}
|
|
@@ -98,6 +98,35 @@ export class LockingFilesystem extends GitGuardedFilesystem {
|
|
|
98
98
|
await validate?.(inputPath, toWrite);
|
|
99
99
|
}));
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Write a file whose new content DEPENDS on what it holds: the read, the
|
|
103
|
+
* computing of the new bytes and the write all happen with the path's lock
|
|
104
|
+
* held, so what lands is derived from the very bytes it replaces.
|
|
105
|
+
*
|
|
106
|
+
* `writeFile` cannot give that. Its content is fixed before the lock is
|
|
107
|
+
* taken, so a caller that read the file first is writing over a state
|
|
108
|
+
* anyone may have moved in between: an `edit_file` replacing an empty owner
|
|
109
|
+
* field with one name would silently overwrite another name written a
|
|
110
|
+
* moment earlier, and both callers would be told their edit landed.
|
|
111
|
+
*
|
|
112
|
+
* `rewrite` receives the file as it is under the lock (null when nothing is
|
|
113
|
+
* there) and answers the bytes to write. Throwing refuses: nothing is
|
|
114
|
+
* written, the lock is released untouched, and the caller gets what was
|
|
115
|
+
* thrown.
|
|
116
|
+
*/
|
|
117
|
+
async rewriteFile(inputPath, rewrite) {
|
|
118
|
+
await this.assertNotGitInternals(inputPath);
|
|
119
|
+
this.assertInsideRepo(inputPath);
|
|
120
|
+
const validate = this.lockContext.validateWrite;
|
|
121
|
+
let toWrite = null;
|
|
122
|
+
return this.withLock(inputPath, () => super.writeFile(inputPath, toWrite), async () => {
|
|
123
|
+
const next = await rewrite(await this.readIfExists(inputPath));
|
|
124
|
+
// The pre-disk gate judges the bytes that will land, as it does for
|
|
125
|
+
// every other write, and here those exist only now.
|
|
126
|
+
await validate?.(inputPath, next);
|
|
127
|
+
toWrite = next;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
101
130
|
async appendFile(inputPath, content) {
|
|
102
131
|
await this.assertNotGitInternals(inputPath);
|
|
103
132
|
this.assertInsideRepo(inputPath);
|