@wbce-d9/api 12.5.10 → 12.5.11

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/app.js CHANGED
@@ -109,6 +109,7 @@ export default async function createApp() {
109
109
  if (env['HSTS_ENABLED']) {
110
110
  app.use(helmet.hsts(getConfigFromEnv('HSTS_', ['HSTS_ENABLED'])));
111
111
  }
112
+ app.use(helmet.crossOriginOpenerPolicy({ policy: env['CROSS_ORIGIN_OPENER_POLICY'] }));
112
113
  await emitter.emitInit('app.before', { app });
113
114
  await emitter.emitInit('middlewares.before', { app });
114
115
  app.use(expressLogger);
@@ -254,6 +254,9 @@ CORS_CREDENTIALS=true
254
254
  # Value for the Access-Control-Max-Age header [18000]
255
255
  CORS_MAX_AGE=18000
256
256
 
257
+ # Value for the Cross-Origin-Opener-Policy header: same-origin, same-origin-allow-popups or unsafe-none ["same-origin"]
258
+ # CROSS_ORIGIN_OPENER_POLICY="same-origin"
259
+
257
260
  ####################################################################################################
258
261
  ### Argon2
259
262
 
package/dist/env.js CHANGED
@@ -63,6 +63,7 @@ const allowedEnvironmentVars = [
63
63
  'IMPORT_IP_DENY_LIST',
64
64
  'CONTENT_SECURITY_POLICY_.+',
65
65
  'HSTS_.+',
66
+ 'CROSS_ORIGIN_OPENER_POLICY',
66
67
  // hashing
67
68
  'HASH_.+',
68
69
  'X_POWERED_BY_ENABLED',
@@ -283,6 +284,7 @@ const defaults = {
283
284
  IP_TRUST_PROXY: true,
284
285
  IP_CUSTOM_HEADER: false,
285
286
  IMPORT_IP_DENY_LIST: ['0.0.0.0', '169.254.169.254'],
287
+ CROSS_ORIGIN_OPENER_POLICY: 'same-origin',
286
288
  SERVE_APP: true,
287
289
  SERVE_GRAPHQL: true,
288
290
  RELATIONAL_BATCH_SIZE: 25000,
@@ -4,7 +4,9 @@ export default defineOperationApi({
4
4
  handler: ({ filter }, { data }) => {
5
5
  const errors = validatePayload(filter, data, { requireAll: true });
6
6
  if (errors.length > 0) {
7
- throw errors;
7
+ // Joi errors hold the validated data ($env, $accountability…) in _original, details[].context and in some
8
+ // messages: only keep the path and type, both derived from the filter, as webhook flows can return them
9
+ throw errors.flatMap((error) => error.details.map(({ path, type }) => ({ path, type })));
8
10
  }
9
11
  else {
10
12
  return null;
@@ -1,9 +1,13 @@
1
1
  import { toArray } from '@wbce-d9/utils';
2
+ import { URL } from 'url';
2
3
  import env from '../env.js';
3
4
  import isUrlAllowed from './is-url-allowed.js';
4
- function isUrlValid(string) {
5
+ // Sentinel origin that never resolves (RFC 6761): relative paths keep it,
6
+ // anything pointing elsewhere (`//x`, `/\x`, `https://x`) does not.
7
+ const RELATIVE_BASE = 'https://relative.invalid';
8
+ function isUrlValid(url, base) {
5
9
  try {
6
- new URL(string);
10
+ new URL(url, base);
7
11
  return true;
8
12
  }
9
13
  catch (err) {
@@ -18,25 +22,20 @@ export function isRedirectAllowedOnLogin(redirect, provider) {
18
22
  return true; // empty redirect
19
23
  if (typeof redirect !== 'string')
20
24
  return false; // invalid type
21
- const publicUrl = env['PUBLIC_URL'];
22
25
  if (!isUrlValid(redirect)) {
23
- if (!redirect.startsWith('//')) {
24
- // should be a relative path like `/admin/test`
25
- return true;
26
- }
27
- // domain without protocol `//example.com/test`
28
- return false;
26
+ // relative path like `/admin/test`, allowed unless it resolves to another origin
27
+ return isUrlValid(redirect, RELATIVE_BASE) && new URL(redirect, RELATIVE_BASE).origin === RELATIVE_BASE;
29
28
  }
30
- const { protocol: redirectProtocol, hostname: redirectDomain } = new URL(redirect);
29
+ const { protocol: redirectProtocol, host: redirectHost } = new URL(redirect);
30
+ const publicUrl = env['PUBLIC_URL'];
31
31
  const envKey = `AUTH_${provider.toUpperCase()}_REDIRECT_ALLOW_LIST`;
32
32
  if (envKey in env) {
33
33
  if (isUrlAllowed(redirect, [...toArray(env[envKey]), publicUrl]))
34
34
  return true;
35
35
  }
36
- if (!isUrlValid(publicUrl)) {
36
+ if (!isUrlValid(publicUrl))
37
37
  return false;
38
- }
39
38
  // allow redirects to the defined PUBLIC_URL
40
- const { protocol: publicProtocol, hostname: publicDomain } = new URL(publicUrl);
41
- return `${redirectProtocol}//${redirectDomain}` === `${publicProtocol}//${publicDomain}`;
39
+ const { protocol: publicProtocol, host: publicHost } = new URL(publicUrl);
40
+ return redirectProtocol === publicProtocol && redirectHost === publicHost;
42
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wbce-d9/api",
3
- "version": "12.5.10",
3
+ "version": "12.5.11",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -144,17 +144,17 @@
144
144
  "tsx": "3.12.6",
145
145
  "uuid-validate": "0.0.3",
146
146
  "@wbce-d9/constants": "10.0.0",
147
+ "@wbce-d9/app": "11.0.0",
147
148
  "@wbce-d9/exceptions": "10.0.0",
148
- "@wbce-d9/extensions-sdk": "10.0.4",
149
- "@wbce-d9/schema": "10.0.0",
150
149
  "@wbce-d9/specs": "9.26.5",
150
+ "@wbce-d9/schema": "10.0.0",
151
151
  "@wbce-d9/storage": "10.0.0",
152
- "@wbce-d9/storage-driver-azure": "10.0.3",
153
- "@wbce-d9/storage-driver-cloudinary": "10.0.4",
154
- "@wbce-d9/app": "11.0.0",
155
152
  "@wbce-d9/storage-driver-gcs": "10.0.3",
153
+ "@wbce-d9/storage-driver-cloudinary": "10.0.4",
154
+ "@wbce-d9/storage-driver-azure": "10.0.3",
156
155
  "@wbce-d9/storage-driver-local": "10.0.3",
157
156
  "@wbce-d9/storage-driver-s3": "10.0.3",
157
+ "@wbce-d9/extensions-sdk": "10.0.4",
158
158
  "@wbce-d9/update-check": "10.0.4",
159
159
  "@wbce-d9/utils": "10.0.3"
160
160
  },