openvibers/openvibe.search Commits
Bound subscribe's delivery secret to Events' 32-256 limit (#7)
Changes
@@@ -5,7 +5,8 @@** node scripts/subscribe.js [--endpoint http://127.0.0.1:4710/internal/events] [--pattern '*.index_document.*']*-- * Reads the environment (.env or /etc/openvibe/search.env):++ * Reads the environment (`.env` here; the service itself gets /etc/openvibe/search.env from systemd,++ * so on the box run `sudo node --env-file=/etc/openvibe/search.env scripts/subscribe.js …`):* EVENTS_URL, OV_NETWORK_INTERNAL_URL, OV_OAUTH_CLIENT_ID, OV_OAUTH_CLIENT_SECRET (the search* principal needs events.subscription.manage for audience openvibe.events)* SEARCH_EVENTS_SECRET the delivery signing secret; the first value is handed to Events, so@@@ -26,7 +27,11 @@ const opt = (name, d) => { const i = args.indexOf(`--${name}`); return i >= 0 ?const pattern = opt('pattern', '*.index_document.*');const secret = config.events.webhookSecrets[0];if (!config.events.url) throw new Error('EVENTS_URL is not set');-- if (!secret || secret.length < 32) throw new Error('SEARCH_EVENTS_SECRET must be set (32+ characters) before subscribing');++ // Events accepts a delivery secret of 32..256 characters and answers 422 for anything longer++ // (POST /api/v1/subscriptions), so refuse it here before the token exchange, where it is visible.++ if (!secret || secret.length < 32 || secret.length > 256) {++ throw new Error('SEARCH_EVENTS_SECRET must be set (32 to 256 characters) before subscribing');++ }if (!config.oauth.clientSecret) throw new Error('OV_OAUTH_CLIENT_SECRET is not set');const tokens = serviceAuth.createTokenClient({tokenUrl: `${config.networkInternalUrl}/oauth/token`,nnew file mode 100644@@@ -0,0 +1,33 @@++'use strict';++// scripts/subscribe.js validates SEARCH_EVENTS_SECRET before it talks to Network or Events: Events++// accepts 32..256 characters, so a secret outside that range is refused here with a clear message++// instead of a late 422. Each case exits before any network call (EVENTS_URL points nowhere useful).++const assert = require('assert');++const { spawnSync } = require('child_process');++const path = require('path');++const { suite } = require('./helpers');++++const t = suite('subscribe');++const script = path.join(__dirname, '..', 'scripts', 'subscribe.js');++++function run(env) {++ return spawnSync(process.execPath, [script], {++ encoding: 'utf8',++ env: { ...process.env, EVENTS_URL: 'http://127.0.0.1:1', OV_OAUTH_CLIENT_SECRET: 'not-a-real-secret', ...env },++ });++}++++t('refuses a secret Events would reject as too long (over 256 characters)', () => {++ const r = run({ SEARCH_EVENTS_SECRET: 'a'.repeat(300) });++ assert.strictEqual(r.status, 1, `expected exit 1, got ${r.status}\n${r.stderr}`);++ assert.match(r.stderr, /SEARCH_EVENTS_SECRET must be set \(32 to 256 characters\)/);++ assert.doesNotMatch(r.stderr, /token endpoint|fetch failed/, 'must fail before any network call');++});++++t('refuses a secret shorter than 32 characters', () => {++ const r = run({ SEARCH_EVENTS_SECRET: 'too-short' });++ assert.strictEqual(r.status, 1, `expected exit 1, got ${r.status}\n${r.stderr}`);++ assert.match(r.stderr, /SEARCH_EVENTS_SECRET must be set \(32 to 256 characters\)/);++});++++t.run();
@@ -5,7 +5,8 @@
@@ -5,7 +5,8 @@
*
*
* node scripts/subscribe.js [--endpoint http://127.0.0.1:4710/internal/events] [--pattern '*.index_document.*']
* node scripts/subscribe.js [--endpoint http://127.0.0.1:4710/internal/events] [--pattern '*.index_document.*']
*
*
- * Reads the environment (.env or /etc/openvibe/search.env):
+ * Reads the environment (`.env` here; the service itself gets /etc/openvibe/search.env from systemd,
+ * so on the box run `sudo node --env-file=/etc/openvibe/search.env scripts/subscribe.js …`):
* EVENTS_URL, OV_NETWORK_INTERNAL_URL, OV_OAUTH_CLIENT_ID, OV_OAUTH_CLIENT_SECRET (the search
* EVENTS_URL, OV_NETWORK_INTERNAL_URL, OV_OAUTH_CLIENT_ID, OV_OAUTH_CLIENT_SECRET (the search
* principal needs events.subscription.manage for audience openvibe.events)
* principal needs events.subscription.manage for audience openvibe.events)
* SEARCH_EVENTS_SECRET the delivery signing secret; the first value is handed to Events, so
* SEARCH_EVENTS_SECRET the delivery signing secret; the first value is handed to Events, so
@@ -26,7 +27,11 @@ const opt = (name, d) => { const i = args.indexOf(`--${name}`); return i >= 0 ?
@@ -26,7 +27,11 @@ const opt = (name, d) => { const i = args.indexOf(`--${name}`); return i >= 0 ?
const pattern = opt('pattern', '*.index_document.*');
const pattern = opt('pattern', '*.index_document.*');
const secret = config.events.webhookSecrets[0];
const secret = config.events.webhookSecrets[0];
if (!config.events.url) throw new Error('EVENTS_URL is not set');
if (!config.events.url) throw new Error('EVENTS_URL is not set');
- if (!secret || secret.length < 32) throw new Error('SEARCH_EVENTS_SECRET must be set (32+ characters) before subscribing');
+ // Events accepts a delivery secret of 32..256 characters and answers 422 for anything longer
+ // (POST /api/v1/subscriptions), so refuse it here before the token exchange, where it is visible.
+ if (!secret || secret.length < 32 || secret.length > 256) {
+ throw new Error('SEARCH_EVENTS_SECRET must be set (32 to 256 characters) before subscribing');
+ }
if (!config.oauth.clientSecret) throw new Error('OV_OAUTH_CLIENT_SECRET is not set');
if (!config.oauth.clientSecret) throw new Error('OV_OAUTH_CLIENT_SECRET is not set');
const tokens = serviceAuth.createTokenClient({
const tokens = serviceAuth.createTokenClient({
tokenUrl: `${config.networkInternalUrl}/oauth/token`,
tokenUrl: `${config.networkInternalUrl}/oauth/token`,
new file mode 100644
new file mode 100644
@@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
+'use strict';
+// scripts/subscribe.js validates SEARCH_EVENTS_SECRET before it talks to Network or Events: Events
+// accepts 32..256 characters, so a secret outside that range is refused here with a clear message
+// instead of a late 422. Each case exits before any network call (EVENTS_URL points nowhere useful).
+const assert = require('assert');
+const { spawnSync } = require('child_process');
+const path = require('path');
+const { suite } = require('./helpers');
+
+const t = suite('subscribe');
+const script = path.join(__dirname, '..', 'scripts', 'subscribe.js');
+
+function run(env) {
+ return spawnSync(process.execPath, [script], {
+ encoding: 'utf8',
+ env: { ...process.env, EVENTS_URL: 'http://127.0.0.1:1', OV_OAUTH_CLIENT_SECRET: 'not-a-real-secret', ...env },
+ });
+}
+
+t('refuses a secret Events would reject as too long (over 256 characters)', () => {
+ const r = run({ SEARCH_EVENTS_SECRET: 'a'.repeat(300) });
+ assert.strictEqual(r.status, 1, `expected exit 1, got ${r.status}\n${r.stderr}`);
+ assert.match(r.stderr, /SEARCH_EVENTS_SECRET must be set \(32 to 256 characters\)/);
+ assert.doesNotMatch(r.stderr, /token endpoint|fetch failed/, 'must fail before any network call');
+});
+
+t('refuses a secret shorter than 32 characters', () => {
+ const r = run({ SEARCH_EVENTS_SECRET: 'too-short' });
+ assert.strictEqual(r.status, 1, `expected exit 1, got ${r.status}\n${r.stderr}`);
+ assert.match(r.stderr, /SEARCH_EVENTS_SECRET must be set \(32 to 256 characters\)/);
+});
+
+t.run();