openvibers/openvibe.live Commits
account-data: pin openvibe-sdk 0.74.0 and guard every table with a person column (#458)
Changes
@@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL<!-- versions:start -->- openvibe-contracts: v0.222.0--- openvibe-sdk: v0.71.0++- openvibe-sdk: v0.74.0- openvibe-shared: v4.48.0<!-- versions:end -->@@@ -6,7 +6,7 @@"port": 3000,"domain": "openvibe.live (www, ingest, whip)","contracts": "openvibe-contracts v0.222.0",-- "sdk": "openvibe-sdk v0.71.0",++ "sdk": "openvibe-sdk v0.74.0","shared": "openvibe-shared v4.48.0","capabilities": ["live.chat_context.read",@@@ -24,7 +24,7 @@"node-media-server": "^2.7.1","onnxruntime-node": "^1.20.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.71.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.48.0","pg": "^8.23.1","sanitize-html": "^2.17.7",@@@ -2856,9 +2856,9 @@}},"node_modules/openvibe-sdk": {-- "version": "0.71.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.71.0",-- "integrity": "sha512-5cGZrMtY6sUsYDiXTw40gn7Lu4LCJI0UvvcO7oWHPAQ+QNN9QeRd4LC8jT67nq2C7GsgS3KN0aDA+3tV2J4avw==",++ "version": "0.74.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",++ "integrity": "sha512-DO9KVPnUriY/I4lX6ZEJtXaUvRDbJyqnqJxvlpl+zkNGFILpmuBXVJogNOCfK071Qci1gg7C4Iw1Xrseklcajg==","license": "MIT","bin": {"openvibe-download": "bin/openvibe-download.js"@@@ -43,7 +43,7 @@"node-media-server": "^2.7.1","onnxruntime-node": "^1.20.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.71.0",++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.48.0","pg": "^8.23.1","sanitize-html": "^2.17.7",@@@ -22,6 +22,7 @@ console.warn = () => {};const express = require('express');const { ids, validate } = require('openvibe-contracts');const { signDeliveryHeaders } = require('openvibe-sdk/events');++const { uncovered } = require('openvibe-sdk/account-data');const db = require('../server/db/database');const DANA = ids.newId('user'), XENA = ids.newId('user'), NOBODY = ids.newId('user');@@@ -62,6 +63,18 @@ const ulid = () => ids.ulid();try {assert.ok(NETWORK_TOPICS.includes('network.account.export_requested') && NETWORK_TOPICS.includes('network.account.deleted'), 'the subscription asks for both');const cols = await accountData.personColumns(d);++ {++ const exempt = {};++ for (const t of accountData.FROZEN) exempt[t] = 'a frozen table Media or Community owns and erases itself';++ for (const t of accountData.RETAIN) exempt[t] = 'a ledger the law keeps: money, moderation or an audit trail';++ const chatTables = ['channel_moderators', 'channel_moderation_settings', 'user_tags', 'chat_ai_summaries', 'chat_timeline_events'];++ for (const t of chatTables) exempt[t] = 'Chat owns and erases it';++ exempt.hidden_relay_users = 'a moderation record of a relay account hidden from a channel, kept for accountability';++ // Every other table with a person column is handled by the schema-driven export and erase below.++ const handled = [...cols.keys()].filter((t) => !chatTables.includes(t));++ const missing = await uncovered(d, { taskKeyed: handled, exempt });++ assert.deepStrictEqual(missing, [], `a table with a person column is neither covered nor exempted: ${missing.join(', ')}`);++ }for (const t of accountData.FROZEN) assert.ok(!cols.has(t), `${t} is never touched`);const existing = new Set((await d.prepare("SELECT table_name AS name FROM information_schema.tables WHERE table_schema = current_schema()").all()).map((r) => r.name));for (const t of cols.keys()) assert.ok(existing.has(t), `account export/deletion references missing table ${t}`);
@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
<!-- versions:start -->
<!-- versions:start -->
- openvibe-contracts: v0.222.0
- openvibe-contracts: v0.222.0
-- openvibe-sdk: v0.71.0
+- openvibe-sdk: v0.74.0
- openvibe-shared: v4.48.0
- openvibe-shared: v4.48.0
<!-- versions:end -->
<!-- versions:end -->
@@ -6,7 +6,7 @@
@@ -6,7 +6,7 @@
"port": 3000,
"port": 3000,
"domain": "openvibe.live (www, ingest, whip)",
"domain": "openvibe.live (www, ingest, whip)",
"contracts": "openvibe-contracts v0.222.0",
"contracts": "openvibe-contracts v0.222.0",
- "sdk": "openvibe-sdk v0.71.0",
+ "sdk": "openvibe-sdk v0.74.0",
"shared": "openvibe-shared v4.48.0",
"shared": "openvibe-shared v4.48.0",
"capabilities": [
"capabilities": [
"live.chat_context.read",
"live.chat_context.read",
@@ -24,7 +24,7 @@
@@ -24,7 +24,7 @@
"node-media-server": "^2.7.1",
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.71.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.48.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.48.0",
"pg": "^8.23.1",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sanitize-html": "^2.17.7",
@@ -2856,9 +2856,9 @@
@@ -2856,9 +2856,9 @@
}
}
},
},
"node_modules/openvibe-sdk": {
"node_modules/openvibe-sdk": {
- "version": "0.71.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.71.0",
- "integrity": "sha512-5cGZrMtY6sUsYDiXTw40gn7Lu4LCJI0UvvcO7oWHPAQ+QNN9QeRd4LC8jT67nq2C7GsgS3KN0aDA+3tV2J4avw==",
+ "version": "0.74.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
+ "integrity": "sha512-DO9KVPnUriY/I4lX6ZEJtXaUvRDbJyqnqJxvlpl+zkNGFILpmuBXVJogNOCfK071Qci1gg7C4Iw1Xrseklcajg==",
"license": "MIT",
"license": "MIT",
"bin": {
"bin": {
"openvibe-download": "bin/openvibe-download.js"
"openvibe-download": "bin/openvibe-download.js"
@@ -43,7 +43,7 @@
@@ -43,7 +43,7 @@
"node-media-server": "^2.7.1",
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.222.0",
- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.71.0",
+ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.74.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.48.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.48.0",
"pg": "^8.23.1",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
"sanitize-html": "^2.17.7",
@@ -22,6 +22,7 @@ console.warn = () => {};
@@ -22,6 +22,7 @@ console.warn = () => {};
const express = require('express');
const express = require('express');
const { ids, validate } = require('openvibe-contracts');
const { ids, validate } = require('openvibe-contracts');
const { signDeliveryHeaders } = require('openvibe-sdk/events');
const { signDeliveryHeaders } = require('openvibe-sdk/events');
+const { uncovered } = require('openvibe-sdk/account-data');
const db = require('../server/db/database');
const db = require('../server/db/database');
const DANA = ids.newId('user'), XENA = ids.newId('user'), NOBODY = ids.newId('user');
const DANA = ids.newId('user'), XENA = ids.newId('user'), NOBODY = ids.newId('user');
@@ -62,6 +63,18 @@ const ulid = () => ids.ulid();
@@ -62,6 +63,18 @@ const ulid = () => ids.ulid();
try {
try {
assert.ok(NETWORK_TOPICS.includes('network.account.export_requested') && NETWORK_TOPICS.includes('network.account.deleted'), 'the subscription asks for both');
assert.ok(NETWORK_TOPICS.includes('network.account.export_requested') && NETWORK_TOPICS.includes('network.account.deleted'), 'the subscription asks for both');
const cols = await accountData.personColumns(d);
const cols = await accountData.personColumns(d);
+ {
+ const exempt = {};
+ for (const t of accountData.FROZEN) exempt[t] = 'a frozen table Media or Community owns and erases itself';
+ for (const t of accountData.RETAIN) exempt[t] = 'a ledger the law keeps: money, moderation or an audit trail';
+ const chatTables = ['channel_moderators', 'channel_moderation_settings', 'user_tags', 'chat_ai_summaries', 'chat_timeline_events'];
+ for (const t of chatTables) exempt[t] = 'Chat owns and erases it';
+ exempt.hidden_relay_users = 'a moderation record of a relay account hidden from a channel, kept for accountability';
+ // Every other table with a person column is handled by the schema-driven export and erase below.
+ const handled = [...cols.keys()].filter((t) => !chatTables.includes(t));
+ const missing = await uncovered(d, { taskKeyed: handled, exempt });
+ assert.deepStrictEqual(missing, [], `a table with a person column is neither covered nor exempted: ${missing.join(', ')}`);
+ }
for (const t of accountData.FROZEN) assert.ok(!cols.has(t), `${t} is never touched`);
for (const t of accountData.FROZEN) assert.ok(!cols.has(t), `${t} is never touched`);
const existing = new Set((await d.prepare("SELECT table_name AS name FROM information_schema.tables WHERE table_schema = current_schema()").all()).map((r) => r.name));
const existing = new Set((await d.prepare("SELECT table_name AS name FROM information_schema.tables WHERE table_schema = current_schema()").all()).map((r) => r.name));
for (const t of cols.keys()) assert.ok(existing.has(t), `account export/deletion references missing table ${t}`);
for (const t of cols.keys()) assert.ok(existing.has(t), `account export/deletion references missing table ${t}`);