addons-scanner-utils 9.15.0 → 10.1.0

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.
@@ -11,8 +11,9 @@ type CreateApiErrorParams = {
11
11
  status?: number;
12
12
  };
13
13
  export declare const createApiError: ({ message, extraInfo, status, }: CreateApiErrorParams) => ApiError;
14
- export type RequestWithFiles = Request & {
14
+ export type RequestWithExtraProps = Request & {
15
15
  xpiFilepath?: string;
16
+ rawBody?: Buffer;
16
17
  };
17
18
  export type FunctionConfig = {
18
19
  _console?: typeof console;
@@ -20,10 +21,9 @@ export type FunctionConfig = {
20
21
  _process?: typeof process;
21
22
  _unlinkFile?: typeof fs.promises.unlink;
22
23
  apiKeyEnvVarName?: string;
23
- requiredApiKeyParam?: string;
24
24
  requiredDownloadUrlParam?: string;
25
25
  tmpDir?: string;
26
26
  xpiFilename?: string;
27
27
  };
28
- export declare const createExpressApp: ({ _console, _fetch, _process, _unlinkFile, apiKeyEnvVarName, requiredApiKeyParam, requiredDownloadUrlParam, tmpDir, xpiFilename, }?: FunctionConfig) => (handler: RequestHandler) => import("express-serve-static-core").Express;
28
+ export declare const createExpressApp: ({ _console, _fetch, _process, _unlinkFile, apiKeyEnvVarName, requiredDownloadUrlParam, tmpDir, xpiFilename, }?: FunctionConfig) => (handler: RequestHandler) => import("express-serve-static-core").Express;
29
29
  export {};
package/dist/functions.js CHANGED
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.createExpressApp = exports.createApiError = void 0;
16
+ const node_crypto_1 = __importDefault(require("node:crypto"));
16
17
  const fs_1 = __importDefault(require("fs"));
17
18
  const os_1 = __importDefault(require("os"));
18
19
  const path_1 = __importDefault(require("path"));
@@ -29,7 +30,7 @@ const createApiError = ({ message, extraInfo, status = 500, }) => {
29
30
  return error;
30
31
  };
31
32
  exports.createApiError = createApiError;
32
- const createExpressApp = ({ _console = console, _fetch = node_fetch_1.default, _process = process, _unlinkFile = fs_1.default.promises.unlink, apiKeyEnvVarName = 'LAMBDA_API_KEY', requiredApiKeyParam = 'api_key', requiredDownloadUrlParam = 'download_url', tmpDir = os_1.default.tmpdir(), xpiFilename = 'input.xpi', } = {}) => (handler) => {
33
+ const createExpressApp = ({ _console = console, _fetch = node_fetch_1.default, _process = process, _unlinkFile = fs_1.default.promises.unlink, apiKeyEnvVarName = 'LAMBDA_API_KEY', requiredDownloadUrlParam = 'download_url', tmpDir = os_1.default.tmpdir(), xpiFilename = 'input.xpi', } = {}) => (handler) => {
33
34
  const app = (0, express_1.default)();
34
35
  const allowedOrigin = _process.env.ALLOWED_ORIGIN || null;
35
36
  if (!allowedOrigin) {
@@ -41,8 +42,16 @@ const createExpressApp = ({ _console = console, _fetch = node_fetch_1.default, _
41
42
  // eslint-disable-next-line no-param-reassign
42
43
  delete _process.env[apiKeyEnvVarName];
43
44
  }
45
+ // This is the options we pass to the `json()` middleware.
46
+ const jsonOptions = {
47
+ // This is a hack to retain the raw body on the request object. We need
48
+ // this to verify the signature of the request.
49
+ verify(req, res, buf) {
50
+ req.rawBody = buf;
51
+ },
52
+ };
44
53
  // Parse JSON body requests.
45
- app.use(body_parser_1.default.json());
54
+ app.use(body_parser_1.default.json(jsonOptions));
46
55
  // This middleware handles the common logic needed to expose our tools. It
47
56
  // adds a new `xpiFilepath` attribute to the Express request or returns an
48
57
  // error that will be converted to an API error by the error handler
@@ -59,17 +68,47 @@ const createExpressApp = ({ _console = console, _fetch = node_fetch_1.default, _
59
68
  }));
60
69
  return;
61
70
  }
62
- if (typeof req.body[requiredApiKeyParam] === 'undefined') {
71
+ if (!apiKey) {
63
72
  next((0, exports.createApiError)({
64
- message: `missing "${requiredApiKeyParam}" parameter`,
73
+ message: `api key must be set`,
74
+ status: 500,
75
+ }));
76
+ return;
77
+ }
78
+ if (!req.get('authorization')) {
79
+ next((0, exports.createApiError)({
80
+ message: `missing authorization header`,
65
81
  status: 400,
66
82
  }));
67
83
  return;
68
84
  }
69
- if (!apiKey || !(0, safe_compare_1.default)(apiKey, req.body[requiredApiKeyParam])) {
85
+ const authorization = String(req.get('authorization'));
86
+ if (authorization.startsWith('HMAC-SHA256 ') && req.rawBody) {
87
+ const digest = node_crypto_1.default
88
+ .createHmac('sha256', apiKey)
89
+ .update(req.rawBody)
90
+ .digest('hex');
91
+ if (!(0, safe_compare_1.default)(`HMAC-SHA256 ${digest}`, authorization)) {
92
+ next((0, exports.createApiError)({
93
+ message: 'authentication has failed',
94
+ status: 401,
95
+ }));
96
+ return;
97
+ }
98
+ }
99
+ else if (authorization.startsWith('Bearer ')) {
100
+ if (!(0, safe_compare_1.default)(`Bearer ${apiKey}`, authorization)) {
101
+ next((0, exports.createApiError)({
102
+ message: 'authentication has failed',
103
+ status: 401,
104
+ }));
105
+ return;
106
+ }
107
+ }
108
+ else {
70
109
  next((0, exports.createApiError)({
71
- message: 'authentication has failed',
72
- status: 401,
110
+ message: 'unsupported authorization scheme',
111
+ status: 400,
73
112
  }));
74
113
  return;
75
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "addons-scanner-utils",
3
- "version": "9.15.0",
3
+ "version": "10.1.0",
4
4
  "description": "Various addons related helpers to build CLIs.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,8 +18,8 @@
18
18
  "yauzl": "2.10.0"
19
19
  },
20
20
  "peerDependencies": {
21
- "body-parser": "1.20.3",
22
- "express": "4.21.2",
21
+ "body-parser": "2.2.2",
22
+ "express": "5.2.1",
23
23
  "node-fetch": "2.6.11",
24
24
  "safe-compare": "1.1.4"
25
25
  },
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/common-tags": "^1.8.0",
42
- "@types/express": "4.17.21",
42
+ "@types/express": "5.0.6",
43
43
  "@types/jest": "^30.0.0",
44
44
  "@types/node": "^25.0.1",
45
45
  "@types/node-fetch": "^2.6.4",
@@ -48,15 +48,15 @@
48
48
  "@types/supertest": "^6.0.2",
49
49
  "@typescript-eslint/eslint-plugin": "^8.46.2",
50
50
  "@typescript-eslint/parser": "^8.46.2",
51
- "body-parser": "1.20.3",
51
+ "body-parser": "2.2.2",
52
52
  "eslint": "^8.1.0",
53
53
  "eslint-config-amo": "^6.1.0",
54
54
  "eslint-import-resolver-typescript": "^4.4.4",
55
55
  "eslint-plugin-amo": "^2.0.0",
56
- "express": "4.21.2",
56
+ "express": "5.2.1",
57
57
  "jest": "^30.2.0",
58
58
  "node-fetch": "2.6.11",
59
- "prettier": "3.7.4",
59
+ "prettier": "3.8.0",
60
60
  "pretty-quick": "4.2.2",
61
61
  "rimraf": "^6.1.0",
62
62
  "safe-compare": "1.1.4",