@open-mercato/webhooks 0.6.5-develop.4674.1.bf258550ce → 0.6.5-develop.4695.1.42ee0ddf0e
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.
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { expect, test } from "@playwright/test";
|
|
2
2
|
import { apiRequest, getAuthToken } from "@open-mercato/core/helpers/integration/api";
|
|
3
3
|
import { readJsonSafe } from "@open-mercato/core/helpers/integration/generalFixtures";
|
|
4
|
+
import { readIntegrationEnvFlag } from "@open-mercato/core/helpers/integration/standaloneEnv";
|
|
4
5
|
import { createWebhookFixture, deleteWebhookIfExists } from "./helpers/fixtures.js";
|
|
5
|
-
const PRIVATE_URLS_ALLOWED =
|
|
6
|
-
(process.env.OM_WEBHOOKS_ALLOW_PRIVATE_URLS ?? "").trim().toLowerCase()
|
|
7
|
-
);
|
|
6
|
+
const PRIVATE_URLS_ALLOWED = readIntegrationEnvFlag("OM_WEBHOOKS_ALLOW_PRIVATE_URLS");
|
|
8
7
|
const ALWAYS_UNSAFE_URLS = [
|
|
9
8
|
"ftp://example.com/webhook",
|
|
10
9
|
"file:///etc/passwd",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/webhooks/__integration__/TC-WEBHOOK-009.spec.ts"],
|
|
4
|
-
"sourcesContent": ["import { expect, test } from '@playwright/test';\nimport { apiRequest, getAuthToken } from '@open-mercato/core/helpers/integration/api';\nimport { readJsonSafe } from '@open-mercato/core/helpers/integration/generalFixtures';\nimport { createWebhookFixture, deleteWebhookIfExists } from './helpers/fixtures';\n\n/**\n * TC-WEBHOOK-009: Invalid and unsafe URL rejection\n * Source: https://github.com/open-mercato/open-mercato/issues/2482\n *\n * Webhook URLs go through the shared outbound-URL safety check. Forbidden schemes,\n * embedded credentials, and malformed URLs are rejected with 400 on both create and\n * update regardless of configuration. Private/loopback hosts are rejected unless the\n * deployment opts in via OM_WEBHOOKS_ALLOW_PRIVATE_URLS (the integration env enables\n * it so the loopback mock receiver works) \u2014 this spec asserts whichever behavior the\n * active flag dictates. Exhaustive private-IP coverage lives in the unit suites\n * (data/__tests__/validators.test.ts, lib/__tests__/url-safety.test.ts).\n */\nconst PRIVATE_URLS_ALLOWED =
|
|
5
|
-
"mappings": "AAAA,SAAS,QAAQ,YAAY;AAC7B,SAAS,YAAY,oBAAoB;AACzC,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB,6BAA6B;AAc5D,MAAM,uBAAuB,
|
|
4
|
+
"sourcesContent": ["import { expect, test } from '@playwright/test';\nimport { apiRequest, getAuthToken } from '@open-mercato/core/helpers/integration/api';\nimport { readJsonSafe } from '@open-mercato/core/helpers/integration/generalFixtures';\nimport { readIntegrationEnvFlag } from '@open-mercato/core/helpers/integration/standaloneEnv';\nimport { createWebhookFixture, deleteWebhookIfExists } from './helpers/fixtures';\n\n/**\n * TC-WEBHOOK-009: Invalid and unsafe URL rejection\n * Source: https://github.com/open-mercato/open-mercato/issues/2482\n *\n * Webhook URLs go through the shared outbound-URL safety check. Forbidden schemes,\n * embedded credentials, and malformed URLs are rejected with 400 on both create and\n * update regardless of configuration. Private/loopback hosts are rejected unless the\n * deployment opts in via OM_WEBHOOKS_ALLOW_PRIVATE_URLS (the integration env enables\n * it so the loopback mock receiver works) \u2014 this spec asserts whichever behavior the\n * active flag dictates. Exhaustive private-IP coverage lives in the unit suites\n * (data/__tests__/validators.test.ts, lib/__tests__/url-safety.test.ts).\n */\nconst PRIVATE_URLS_ALLOWED = readIntegrationEnvFlag('OM_WEBHOOKS_ALLOW_PRIVATE_URLS');\n\nconst ALWAYS_UNSAFE_URLS = [\n 'ftp://example.com/webhook',\n 'file:///etc/passwd',\n 'http://user:pass@example.com/webhook',\n 'not-a-valid-url',\n];\n\nconst PRIVATE_HOST_URLS = [\n 'http://127.0.0.1:9001/webhook',\n 'http://10.0.0.1/webhook',\n 'http://[::1]:9001/webhook',\n];\n\nfunction createBody(url: string) {\n return {\n name: `Webhook URL Safety ${Date.now()}`,\n url,\n subscribedEvents: ['catalog.product.created'],\n };\n}\n\ntest.describe('TC-WEBHOOK-009: Invalid and unsafe URL rejection', () => {\n test('should reject unsafe webhook URLs on create and update and accept valid external URLs', async ({ request }) => {\n const token = await getAuthToken(request);\n let webhookId: string | null = null;\n const strayPrivateWebhookIds: string[] = [];\n\n try {\n // Always-unsafe URLs are rejected at create regardless of the private-URL flag.\n for (const url of ALWAYS_UNSAFE_URLS) {\n const response = await apiRequest(request, 'POST', '/api/webhooks', { token, data: createBody(url) });\n expect(response.status(), `POST should reject unsafe url ${url}`).toBe(400);\n }\n\n // A valid external https URL is accepted.\n const created = await createWebhookFixture(request, token, {\n name: `Webhook URL Safety Valid ${Date.now()}`,\n url: 'https://example.com/webhook-url-safety',\n subscribedEvents: ['catalog.product.created'],\n });\n webhookId = created.id;\n\n // Updating an existing webhook to an unsafe URL is rejected; the record is unchanged.\n for (const url of ALWAYS_UNSAFE_URLS) {\n const response = await apiRequest(request, 'PUT', `/api/webhooks/${created.id}`, { token, data: { url } });\n expect(response.status(), `PUT should reject unsafe url ${url}`).toBe(400);\n }\n\n // Updating to another valid external URL succeeds.\n const validUpdate = await apiRequest(request, 'PUT', `/api/webhooks/${created.id}`, {\n token,\n data: { url: 'https://example.org/webhook-url-safety-updated' },\n });\n expect(validUpdate.status()).toBe(200);\n\n // Private/loopback hosts: behavior depends on the deployment flag.\n for (const url of PRIVATE_HOST_URLS) {\n const response = await apiRequest(request, 'POST', '/api/webhooks', { token, data: createBody(url) });\n if (PRIVATE_URLS_ALLOWED) {\n expect(response.status(), `POST should accept private url ${url} when allowed`).toBe(201);\n const body = await readJsonSafe<{ id?: string }>(response);\n if (body?.id) strayPrivateWebhookIds.push(body.id);\n } else {\n expect(response.status(), `POST should reject private url ${url} when not allowed`).toBe(400);\n }\n }\n } finally {\n await deleteWebhookIfExists(request, token, webhookId);\n for (const id of strayPrivateWebhookIds) {\n await deleteWebhookIfExists(request, token, id);\n }\n }\n });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,QAAQ,YAAY;AAC7B,SAAS,YAAY,oBAAoB;AACzC,SAAS,oBAAoB;AAC7B,SAAS,8BAA8B;AACvC,SAAS,sBAAsB,6BAA6B;AAc5D,MAAM,uBAAuB,uBAAuB,gCAAgC;AAEpF,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,WAAW,KAAa;AAC/B,SAAO;AAAA,IACL,MAAM,sBAAsB,KAAK,IAAI,CAAC;AAAA,IACtC;AAAA,IACA,kBAAkB,CAAC,yBAAyB;AAAA,EAC9C;AACF;AAEA,KAAK,SAAS,oDAAoD,MAAM;AACtE,OAAK,yFAAyF,OAAO,EAAE,QAAQ,MAAM;AACnH,UAAM,QAAQ,MAAM,aAAa,OAAO;AACxC,QAAI,YAA2B;AAC/B,UAAM,yBAAmC,CAAC;AAE1C,QAAI;AAEF,iBAAW,OAAO,oBAAoB;AACpC,cAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,iBAAiB,EAAE,OAAO,MAAM,WAAW,GAAG,EAAE,CAAC;AACpG,eAAO,SAAS,OAAO,GAAG,iCAAiC,GAAG,EAAE,EAAE,KAAK,GAAG;AAAA,MAC5E;AAGA,YAAM,UAAU,MAAM,qBAAqB,SAAS,OAAO;AAAA,QACzD,MAAM,4BAA4B,KAAK,IAAI,CAAC;AAAA,QAC5C,KAAK;AAAA,QACL,kBAAkB,CAAC,yBAAyB;AAAA,MAC9C,CAAC;AACD,kBAAY,QAAQ;AAGpB,iBAAW,OAAO,oBAAoB;AACpC,cAAM,WAAW,MAAM,WAAW,SAAS,OAAO,iBAAiB,QAAQ,EAAE,IAAI,EAAE,OAAO,MAAM,EAAE,IAAI,EAAE,CAAC;AACzG,eAAO,SAAS,OAAO,GAAG,gCAAgC,GAAG,EAAE,EAAE,KAAK,GAAG;AAAA,MAC3E;AAGA,YAAM,cAAc,MAAM,WAAW,SAAS,OAAO,iBAAiB,QAAQ,EAAE,IAAI;AAAA,QAClF;AAAA,QACA,MAAM,EAAE,KAAK,iDAAiD;AAAA,MAChE,CAAC;AACD,aAAO,YAAY,OAAO,CAAC,EAAE,KAAK,GAAG;AAGrC,iBAAW,OAAO,mBAAmB;AACnC,cAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,iBAAiB,EAAE,OAAO,MAAM,WAAW,GAAG,EAAE,CAAC;AACpG,YAAI,sBAAsB;AACxB,iBAAO,SAAS,OAAO,GAAG,kCAAkC,GAAG,eAAe,EAAE,KAAK,GAAG;AACxF,gBAAM,OAAO,MAAM,aAA8B,QAAQ;AACzD,cAAI,MAAM,GAAI,wBAAuB,KAAK,KAAK,EAAE;AAAA,QACnD,OAAO;AACL,iBAAO,SAAS,OAAO,GAAG,kCAAkC,GAAG,mBAAmB,EAAE,KAAK,GAAG;AAAA,QAC9F;AAAA,MACF;AAAA,IACF,UAAE;AACA,YAAM,sBAAsB,SAAS,OAAO,SAAS;AACrD,iBAAW,MAAM,wBAAwB;AACvC,cAAM,sBAAsB,SAAS,OAAO,EAAE;AAAA,MAChD;AAAA,IACF;AAAA,EACF,CAAC;AACH,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/webhooks",
|
|
3
|
-
"version": "0.6.5-develop.
|
|
3
|
+
"version": "0.6.5-develop.4695.1.42ee0ddf0e",
|
|
4
4
|
"description": "Webhooks module for Open Mercato — Standard Webhooks compliant outbound/inbound delivery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -69,19 +69,19 @@
|
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@open-mercato/core": "0.6.5-develop.
|
|
73
|
-
"@open-mercato/queue": "0.6.5-develop.
|
|
74
|
-
"@open-mercato/ui": "0.6.5-develop.
|
|
72
|
+
"@open-mercato/core": "0.6.5-develop.4695.1.42ee0ddf0e",
|
|
73
|
+
"@open-mercato/queue": "0.6.5-develop.4695.1.42ee0ddf0e",
|
|
74
|
+
"@open-mercato/ui": "0.6.5-develop.4695.1.42ee0ddf0e",
|
|
75
75
|
"svix": "^1.95.1"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"@mikro-orm/postgresql": "^7.0.14",
|
|
79
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
79
|
+
"@open-mercato/shared": "0.6.5-develop.4695.1.42ee0ddf0e",
|
|
80
80
|
"react": "^19.0.0",
|
|
81
81
|
"react-dom": "^19.0.0"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@open-mercato/shared": "0.6.5-develop.
|
|
84
|
+
"@open-mercato/shared": "0.6.5-develop.4695.1.42ee0ddf0e",
|
|
85
85
|
"@types/jest": "^30.0.0",
|
|
86
86
|
"@types/react": "^19.2.16",
|
|
87
87
|
"@types/react-dom": "^19.2.3",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { expect, test } from '@playwright/test';
|
|
2
2
|
import { apiRequest, getAuthToken } from '@open-mercato/core/helpers/integration/api';
|
|
3
3
|
import { readJsonSafe } from '@open-mercato/core/helpers/integration/generalFixtures';
|
|
4
|
+
import { readIntegrationEnvFlag } from '@open-mercato/core/helpers/integration/standaloneEnv';
|
|
4
5
|
import { createWebhookFixture, deleteWebhookIfExists } from './helpers/fixtures';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -15,9 +16,7 @@ import { createWebhookFixture, deleteWebhookIfExists } from './helpers/fixtures'
|
|
|
15
16
|
* active flag dictates. Exhaustive private-IP coverage lives in the unit suites
|
|
16
17
|
* (data/__tests__/validators.test.ts, lib/__tests__/url-safety.test.ts).
|
|
17
18
|
*/
|
|
18
|
-
const PRIVATE_URLS_ALLOWED =
|
|
19
|
-
(process.env.OM_WEBHOOKS_ALLOW_PRIVATE_URLS ?? '').trim().toLowerCase(),
|
|
20
|
-
);
|
|
19
|
+
const PRIVATE_URLS_ALLOWED = readIntegrationEnvFlag('OM_WEBHOOKS_ALLOW_PRIVATE_URLS');
|
|
21
20
|
|
|
22
21
|
const ALWAYS_UNSAFE_URLS = [
|
|
23
22
|
'ftp://example.com/webhook',
|