graphile-settings 6.14.0 → 6.15.1

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.
@@ -29,6 +29,7 @@ import Streamer from '@constructive-io/s3-streamer';
29
29
  import { Logger } from '@pgpmjs/logger';
30
30
  import { createHash, randomUUID } from 'crypto';
31
31
  import { finalizeStagedUpload, resolveManagedUploadTarget, withRequestPgClient, } from 'graphile-presigned-url-plugin';
32
+ import { checkTypeAgreement } from 'mime-bytes';
32
33
  import { Transform } from 'stream';
33
34
  import { createBucketNameResolver, createEnsureBucketProvisioned, getPresignedUrlS3Config, } from './presigned-url-resolver';
34
35
  const log = new Logger('upload-resolver');
@@ -178,6 +179,21 @@ async function uploadResolver(upload, _args, context, info) {
178
179
  readStream: upload.createReadStream(),
179
180
  filename,
180
181
  });
182
+ // The three claims must agree, and disagreement is rejection rather than
183
+ // relabelling: an `.jpg` carrying HTML is served to a browser as an image and
184
+ // executed as a script. Compared against `magic.type` — what the bytes say —
185
+ // not `contentType`, which the detector may have refined using the very
186
+ // extension under suspicion.
187
+ const agreement = checkTypeAgreement({
188
+ filename,
189
+ declaredMime: upload.mimetype,
190
+ detectedMime: detected.magic?.type,
191
+ });
192
+ if (!agreement.ok) {
193
+ detected.stream.destroy();
194
+ throw new Error(`UPLOAD_TYPE_MISMATCH: ${agreement.violation.message}. The upload was rejected rather than ` +
195
+ 'stored under a type it is not.');
196
+ }
181
197
  const allowed = allowedMimeTypes(tags, typ);
182
198
  if (allowed.length && !allowed.includes(detected.contentType)) {
183
199
  detected.stream.destroy();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphile-settings",
3
- "version": "6.14.0",
3
+ "version": "6.15.1",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "graphile settings",
6
6
  "main": "index.js",
@@ -31,46 +31,47 @@
31
31
  "dependencies": {
32
32
  "@aws-sdk/client-s3": "^3.1052.0",
33
33
  "@constructive-io/bucket-provisioner": "^0.24.0",
34
- "@constructive-io/graphql-env": "^3.29.3",
35
- "@constructive-io/graphql-types": "^3.28.3",
36
- "@constructive-io/s3-streamer": "^2.40.0",
34
+ "@constructive-io/graphql-env": "^3.29.4",
35
+ "@constructive-io/graphql-types": "^3.28.4",
36
+ "@constructive-io/s3-streamer": "^2.40.2",
37
37
  "@constructive-io/s3-utils": "^2.30.0",
38
38
  "@constructive-io/upload-names": "^2.29.0",
39
39
  "@dataplan/json": "1.0.1",
40
40
  "@dataplan/pg": "1.1.1",
41
41
  "@graphile-contrib/pg-many-to-many": "2.0.0-rc.2",
42
42
  "@pgpmjs/logger": "^2.24.1",
43
- "@pgpmjs/types": "^2.50.3",
43
+ "@pgpmjs/types": "^2.50.4",
44
44
  "@pgsql/quotes": "^18.2.4",
45
45
  "cors": "^2.8.6",
46
46
  "express": "^5.2.1",
47
- "grafast": "1.1.1",
47
+ "grafast": "1.1.2",
48
48
  "grafserv": "1.0.1",
49
49
  "graphile-bucket-provisioner-plugin": "1.12.4",
50
50
  "graphile-build": "5.1.1",
51
51
  "graphile-build-pg": "5.1.3",
52
- "graphile-bulk-mutations": "^1.11.7",
52
+ "graphile-bulk-mutations": "^1.11.9",
53
53
  "graphile-config": "1.1.0",
54
- "graphile-connection-filter": "^2.11.7",
55
- "graphile-history": "^1.3.7",
56
- "graphile-i18n": "^2.11.7",
57
- "graphile-llm": "^1.11.7",
58
- "graphile-ltree": "^2.11.7",
54
+ "graphile-connection-filter": "^2.11.9",
55
+ "graphile-history": "^1.3.9",
56
+ "graphile-i18n": "^2.11.9",
57
+ "graphile-llm": "^1.11.9",
58
+ "graphile-ltree": "^2.11.9",
59
59
  "graphile-meta": "^1.7.1",
60
- "graphile-pg-aggregates": "^2.11.7",
61
- "graphile-postgis": "^3.11.7",
62
- "graphile-presigned-url-plugin": "^1.12.0",
60
+ "graphile-pg-aggregates": "^2.11.9",
61
+ "graphile-postgis": "^3.11.9",
62
+ "graphile-presigned-url-plugin": "^1.13.0",
63
63
  "graphile-realtime-subscriptions": "^1.10.5",
64
- "graphile-search": "^2.11.7",
64
+ "graphile-search": "^2.11.9",
65
65
  "graphile-upload-plugin": "^2.25.0",
66
66
  "graphile-utils": "5.0.3",
67
67
  "graphql": "16.13.0",
68
68
  "inflekt": "^0.8.1",
69
69
  "lru-cache": "^11.2.7",
70
+ "mime-bytes": "^0.31.0",
70
71
  "pg": "^8.21.0",
71
72
  "pg-query-context": "^2.29.1",
72
73
  "pg-sql2": "5.0.1",
73
- "postgraphile": "5.1.3",
74
+ "postgraphile": "5.1.4",
74
75
  "request-ip": "^3.3.0",
75
76
  "tamedevil": "0.1.1"
76
77
  },
@@ -79,7 +80,7 @@
79
80
  "@types/express": "^5.0.6",
80
81
  "@types/pg": "^8.20.4",
81
82
  "@types/request-ip": "^0.0.41",
82
- "graphile-test": "^5.11.7",
83
+ "graphile-test": "^5.11.9",
83
84
  "makage": "^0.3.0",
84
85
  "nodemon": "^3.1.14",
85
86
  "ts-node": "^10.9.2"
@@ -91,5 +92,5 @@
91
92
  "constructive",
92
93
  "graphql"
93
94
  ],
94
- "gitHead": "44f56ee80a348f649a22e5bd32b35f2822726509"
95
+ "gitHead": "eacbe10ce544848c93a4c17fc8a8474d1a788a18"
95
96
  }
@@ -35,6 +35,7 @@ const s3_streamer_1 = __importDefault(require("@constructive-io/s3-streamer"));
35
35
  const logger_1 = require("@pgpmjs/logger");
36
36
  const crypto_1 = require("crypto");
37
37
  const graphile_presigned_url_plugin_1 = require("graphile-presigned-url-plugin");
38
+ const mime_bytes_1 = require("mime-bytes");
38
39
  const stream_1 = require("stream");
39
40
  const presigned_url_resolver_1 = require("./presigned-url-resolver");
40
41
  const log = new logger_1.Logger('upload-resolver');
@@ -184,6 +185,21 @@ async function uploadResolver(upload, _args, context, info) {
184
185
  readStream: upload.createReadStream(),
185
186
  filename,
186
187
  });
188
+ // The three claims must agree, and disagreement is rejection rather than
189
+ // relabelling: an `.jpg` carrying HTML is served to a browser as an image and
190
+ // executed as a script. Compared against `magic.type` — what the bytes say —
191
+ // not `contentType`, which the detector may have refined using the very
192
+ // extension under suspicion.
193
+ const agreement = (0, mime_bytes_1.checkTypeAgreement)({
194
+ filename,
195
+ declaredMime: upload.mimetype,
196
+ detectedMime: detected.magic?.type,
197
+ });
198
+ if (!agreement.ok) {
199
+ detected.stream.destroy();
200
+ throw new Error(`UPLOAD_TYPE_MISMATCH: ${agreement.violation.message}. The upload was rejected rather than ` +
201
+ 'stored under a type it is not.');
202
+ }
187
203
  const allowed = allowedMimeTypes(tags, typ);
188
204
  if (allowed.length && !allowed.includes(detected.contentType)) {
189
205
  detected.stream.destroy();