From 30810fd19f4c7e4eb13583aa4000c5b4da033028 Mon Sep 17 00:00:00 2001 From: Andrey Gruzdev Date: Fri, 29 May 2026 14:42:21 +0200 Subject: [PATCH 1/5] fix(security): harden verifier command execution --- nest/package-lock.json | 842 +++++++++--------- nest/src/constants/validation.constants.ts | 25 + .../controllers/verify/verify.controller.ts | 39 +- nest/src/dtos/verify.dto.ts | 27 +- .../compiler/compiler.service.spec.ts | 25 +- .../src/services/compiler/compiler.service.ts | 29 +- nest/src/services/exec/exec.service.spec.ts | 32 +- nest/src/services/exec/exec.service.ts | 134 ++- .../services/github/github.service.spec.ts | 42 +- nest/src/services/github/github.service.ts | 94 +- 10 files changed, 793 insertions(+), 496 deletions(-) create mode 100644 nest/src/constants/validation.constants.ts diff --git a/nest/package-lock.json b/nest/package-lock.json index d4e0f84..1cee524 100644 --- a/nest/package-lock.json +++ b/nest/package-lock.json @@ -43,16 +43,16 @@ } }, "node_modules/@angular-devkit/core": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", - "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.24.tgz", + "integrity": "sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", + "picomatch": "4.0.4", "rxjs": "7.8.1", "source-map": "0.7.4" }, @@ -81,13 +81,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", - "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.24.tgz", + "integrity": "sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.19", + "@angular-devkit/core": "19.2.24", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "5.4.1", @@ -100,14 +100,14 @@ } }, "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", - "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.24.tgz", + "integrity": "sha512-bsStZQG67J1HBqTmWxtIcobvgrn32L4UOdL7hGyOru5VxDWPNA8pRnDYavT3hnJeBkJYPoQIw8u7Dm0ecoQprw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.19", - "@angular-devkit/schematics": "19.2.19", + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", "@inquirer/prompts": "7.3.2", "ansi-colors": "4.1.3", "symbol-observable": "4.0.0", @@ -679,9 +679,9 @@ "license": "MIT" }, "node_modules/@borewit/text-codec": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", - "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "license": "MIT", "funding": { "type": "github", @@ -841,9 +841,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -1303,27 +1303,6 @@ } } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1765,9 +1744,9 @@ } }, "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -1803,13 +1782,13 @@ "license": "ISC" }, "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2225,15 +2204,15 @@ } }, "node_modules/@nestjs/cli": { - "version": "11.0.14", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.14.tgz", - "integrity": "sha512-YwP03zb5VETTwelXU+AIzMVbEZKk/uxJL+z9pw0mdG9ogAtqZ6/mpmIM4nEq/NU8D0a7CBRLcMYUmWW/55pfqw==", + "version": "11.0.21", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.21.tgz", + "integrity": "sha512-F8mV0Sj/zVEouzR3NxBuJy08YHTUOmC5Xdcx3qIIaJWzrm8Vw86CHkhkaPBJ5ewRMHPDCShPmhsfwhpCcjts3A==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.19", - "@angular-devkit/schematics": "19.2.19", - "@angular-devkit/schematics-cli": "19.2.19", + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "@angular-devkit/schematics-cli": "19.2.24", "@inquirer/prompts": "7.10.1", "@nestjs/schematics": "^11.0.1", "ansis": "4.2.0", @@ -2241,13 +2220,13 @@ "cli-table3": "0.6.5", "commander": "4.1.1", "fork-ts-checker-webpack-plugin": "9.1.0", - "glob": "13.0.0", + "glob": "13.0.6", "node-emoji": "1.11.0", "ora": "5.4.1", "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.2.0", "typescript": "5.9.3", - "webpack": "5.103.0", + "webpack": "5.106.0", "webpack-node-externals": "3.0.0" }, "bin": { @@ -2257,7 +2236,7 @@ "node": ">= 20.11" }, "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0", "@swc/core": "^1.3.62" }, "peerDependenciesMeta": { @@ -2270,12 +2249,12 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.9", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.9.tgz", - "integrity": "sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==", + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.24.tgz", + "integrity": "sha512-9zHxaDDM+oXW9As6UsP5yYB+UqczBmpeSCIFWdPEtEukMnZhxODG1BBjaUcdBB8Sc1uzojSJSJlp3yFp853t1g==", "license": "MIT", "dependencies": { - "file-type": "21.1.0", + "file-type": "21.3.4", "iterare": "1.2.1", "load-esm": "1.0.3", "tslib": "2.8.1", @@ -2301,16 +2280,16 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.9", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.9.tgz", - "integrity": "sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==", + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.24.tgz", + "integrity": "sha512-K4bzT+lEdd0Hhcsw3jtk56QAW6s6skK3ViN7hIROSN0kUf4ROwWEAKopJID6yhPQxB45kDtP2wEcjzE8171J3g==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "path-to-regexp": "8.3.0", + "path-to-regexp": "8.4.2", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -2342,14 +2321,14 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", - "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.1.tgz", + "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==", "license": "MIT", "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "class-transformer": "^0.4.0 || ^0.5.0", - "class-validator": "^0.13.0 || ^0.14.0", + "class-validator": "^0.13.0 || ^0.14.0 || ^0.15.0", "reflect-metadata": "^0.1.12 || ^0.2.0" }, "peerDependenciesMeta": { @@ -2362,15 +2341,15 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.9", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.9.tgz", - "integrity": "sha512-GVd3+0lO0mJq2m1kl9hDDnVrX3Nd4oH3oDfklz0pZEVEVS0KVSp63ufHq2Lu9cyPdSBuelJr9iPm2QQ1yX+Kmw==", + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.24.tgz", + "integrity": "sha512-CeMKbRBm05aOBiWhIHWO2xDeHbxynBF9ySQv3gRjObz2N5+uJnYriAYkHvVqvC4JIydmMPmT5VdICFNlNz3qyA==", "license": "MIT", "dependencies": { - "cors": "2.8.5", - "express": "5.1.0", - "multer": "2.0.2", - "path-to-regexp": "8.3.0", + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.1.1", + "path-to-regexp": "8.4.2", "tslib": "2.8.1" }, "funding": { @@ -2396,94 +2375,43 @@ } }, "node_modules/@nestjs/schematics": { - "version": "11.0.9", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", - "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.1.0.tgz", + "integrity": "sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.17", - "@angular-devkit/schematics": "19.2.17", - "comment-json": "4.4.1", + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "comment-json": "5.0.0", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, "peerDependencies": { + "prettier": "^3.0.0", "typescript": ">=4.8.2" - } - }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", - "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" }, "peerDependenciesMeta": { - "chokidar": { + "prettier": { "optional": true } } }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", - "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.17", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, "node_modules/@nestjs/swagger": { - "version": "11.2.3", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.3.tgz", - "integrity": "sha512-a0xFfjeqk69uHIUpP8u0ryn4cKuHdra2Ug96L858i0N200Hxho+n3j+TlQXyOF4EstLSGjTfxI1Xb2E1lUxeNg==", + "version": "11.4.4", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.4.4.tgz", + "integrity": "sha512-VaIo1ruV2G7b+f2zPzkBSUNy9a/WQ9sg8TLKhWlrTfg4O6U10M/PA7Xi6XMXadOVhwOqoesijba8jH3i/3adrA==", "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.16.0", - "@nestjs/mapped-types": "2.1.0", + "@nestjs/mapped-types": "2.1.1", "js-yaml": "4.1.1", - "lodash": "4.17.21", - "path-to-regexp": "8.3.0", - "swagger-ui-dist": "5.30.2" + "lodash": "4.18.1", + "path-to-regexp": "8.4.2", + "swagger-ui-dist": "5.32.6" }, "peerDependencies": { - "@fastify/static": "^8.0.0", + "@fastify/static": "^8.0.0 || ^9.0.0", "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", "class-transformer": "*", @@ -2641,14 +2569,13 @@ } }, "node_modules/@tokenizer/inflate": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.3.1.tgz", - "integrity": "sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", "license": "MIT", "dependencies": { - "debug": "^4.4.1", - "fflate": "^0.8.2", - "token-types": "^6.0.0" + "debug": "^4.4.3", + "token-types": "^6.1.1" }, "engines": { "node": ">=18" @@ -3136,9 +3063,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -3146,13 +3073,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3668,9 +3595,9 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -3703,10 +3630,22 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { @@ -3828,9 +3767,9 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -3866,14 +3805,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/babel-jest": { @@ -4004,13 +3944,16 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", - "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/bl": { @@ -4026,15 +3969,15 @@ } }, "node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", - "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -4043,7 +3986,7 @@ "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", - "qs": "^6.14.0", + "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" }, @@ -4062,9 +4005,9 @@ "license": "Apache-2.0" }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "dev": true, "license": "MIT", "dependencies": { @@ -4092,9 +4035,9 @@ "license": "MIT" }, "node_modules/browserslist": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", - "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -4112,11 +4055,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.25", - "caniuse-lite": "^1.0.30001754", - "electron-to-chromium": "^1.5.249", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.1.4" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -4249,9 +4192,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001757", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", - "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", "dev": true, "funding": [ { @@ -4525,14 +4468,13 @@ } }, "node_modules/comment-json": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", - "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", + "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", "dev": true, "license": "MIT", "dependencies": { "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", "esprima": "^4.0.1" }, "engines": { @@ -4571,9 +4513,9 @@ } }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -4617,17 +4559,10 @@ "node": ">=6.6.0" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" - }, "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -4635,6 +4570,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/cosmiconfig": { @@ -4810,9 +4749,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.262", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz", - "integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==", + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", "dev": true, "license": "ISC" }, @@ -4861,14 +4800,14 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz", + "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -4903,9 +4842,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, @@ -5103,9 +5042,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -5289,18 +5228,19 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.0", + "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", + "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -5365,9 +5305,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -5409,12 +5349,6 @@ } } }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5429,14 +5363,14 @@ } }, "node_modules/file-type": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.0.tgz", - "integrity": "sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==", + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", "license": "MIT", "dependencies": { - "@tokenizer/inflate": "^0.3.1", - "strtok3": "^10.3.1", - "token-types": "^6.0.0", + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" }, "engines": { @@ -5512,16 +5446,16 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -5790,17 +5724,17 @@ } }, "node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5826,16 +5760,37 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/glob/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5881,9 +5836,9 @@ "license": "MIT" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6018,6 +5973,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6538,9 +6506,9 @@ } }, "node_modules/jest-config/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -6576,13 +6544,13 @@ "license": "ISC" }, "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6895,9 +6863,9 @@ } }, "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -6933,13 +6901,13 @@ "license": "ISC" }, "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -7299,9 +7267,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.memoize": { @@ -7468,9 +7436,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -7528,9 +7496,9 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -7544,32 +7512,21 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7577,21 +7534,22 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" + "type-is": "^1.6.18" }, "engines": { "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/multer/node_modules/media-typer": { @@ -7860,11 +7818,14 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -8109,34 +8070,34 @@ } }, "node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -8161,9 +8122,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -8343,10 +8304,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -8376,9 +8340,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -8390,16 +8354,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -8629,9 +8583,9 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -8691,41 +8645,35 @@ } }, "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.1" + "statuses": "^2.0.2" }, "engines": { "node": ">= 18" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -8735,6 +8683,10 @@ }, "engines": { "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -8786,13 +8738,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -9054,9 +9006,9 @@ } }, "node_modules/strtok3": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", - "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0" @@ -9083,9 +9035,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.30.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz", - "integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==", + "version": "5.32.6", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.6.tgz", + "integrity": "sha512-75ttZNaYCLoFPnozPZcTUU6mS3wKT8l7WLjU5zJSHFeJa23i5vtnze6IiCl4jDMPeQTXVXIgovq4M11NNfQvSA==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" @@ -9118,9 +9070,9 @@ } }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -9132,9 +9084,9 @@ } }, "node_modules/terser": { - "version": "5.44.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", - "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9151,16 +9103,15 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -9174,12 +9125,39 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, "@swc/core": { "optional": true }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, "esbuild": { "optional": true }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, "uglify-js": { "optional": true } @@ -9336,19 +9314,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -9379,12 +9344,12 @@ } }, "node_modules/token-types": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", - "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", "license": "MIT", "dependencies": { - "@borewit/text-codec": "^0.1.0", + "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, @@ -9565,17 +9530,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typedarray": { @@ -9722,9 +9704,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -9812,9 +9794,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", "dependencies": { @@ -9842,9 +9824,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.103.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", - "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", + "version": "5.106.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.0.tgz", + "integrity": "sha512-Pkx5joZ9RrdgO5LBkyX1L2ZAJeK/Taz3vqZ9CbcP0wS5LEMx5QkKsEwLl29QJfihZ+DKRBFldzy1O30pJ1MDpA==", "dev": true, "license": "MIT", "dependencies": { @@ -9854,12 +9836,12 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.26.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.3", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -9870,9 +9852,9 @@ "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -9901,9 +9883,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", "dev": true, "license": "MIT", "engines": { diff --git a/nest/src/constants/validation.constants.ts b/nest/src/constants/validation.constants.ts new file mode 100644 index 0000000..79efb8a --- /dev/null +++ b/nest/src/constants/validation.constants.ts @@ -0,0 +1,25 @@ +export const NETWORK_IDS = ['mainnet', 'testnet'] as const; + +export type NetworkId = (typeof NETWORK_IDS)[number]; + +export const COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i; + +const NAMED_ACCOUNT_PATTERN = + /^(?=.{2,64}$)(?:[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?))*$/; +const IMPLICIT_ACCOUNT_PATTERN = /^[0-9a-f]{64}$/; + +export const NEAR_ACCOUNT_ID_PATTERN = new RegExp( + `${NAMED_ACCOUNT_PATTERN.source}|${IMPLICIT_ACCOUNT_PATTERN.source}`, +); + +export function isValidNetworkId(networkId: string): networkId is NetworkId { + return NETWORK_IDS.includes(networkId as NetworkId); +} + +export function isValidCommitSha(sha: string): boolean { + return COMMIT_SHA_PATTERN.test(sha); +} + +export function isValidNearAccountId(accountId: string): boolean { + return NEAR_ACCOUNT_ID_PATTERN.test(accountId); +} diff --git a/nest/src/controllers/verify/verify.controller.ts b/nest/src/controllers/verify/verify.controller.ts index cd7f5eb..71c96d3 100644 --- a/nest/src/controllers/verify/verify.controller.ts +++ b/nest/src/controllers/verify/verify.controller.ts @@ -163,7 +163,7 @@ export class VerifyController { ); } } catch (error) { - if (error.status === 400) { + if (error instanceof HttpException && error.getStatus() === 400) { throw error; // Re-throw validation errors } this.logger.error(`Pre-verification checks failed: ${error.message}`); @@ -232,22 +232,33 @@ export class VerifyController { buildInfo.source_code_snapshot, ); - // Create a temporary folder to clone the repository for IPFS - const tempFolder = await this.tempService.createFolder(); + let tempFolder: string = null; + let cid = ''; - // Clone the repository and checkout the commit - await this.githubService.clone(tempFolder, repoUrl); - const repoPath = this.githubService.getRepoPath(tempFolder, repoUrl); - await this.githubService.checkout(repoPath, sha); + try { + // Create a temporary folder to clone the repository for IPFS + tempFolder = await this.tempService.createFolder(); - // Pin to IPFS - this.logger.log(`Adding repository to IPFS for ${accountId}`); - let cid = ''; - cid = await this.ipfsService.addFolder(repoPath); - this.logger.log(`IPFS CID for ${accountId}: ${cid}`); + // Clone the repository and checkout the commit + await this.githubService.clone(tempFolder, repoUrl); + const repoPath = this.githubService.getRepoPath(tempFolder, repoUrl); + await this.githubService.checkout(repoPath, sha); - // Clean up temp folder - await this.tempService.deleteFolder(tempFolder); + // Pin to IPFS + this.logger.log(`Adding repository to IPFS for ${accountId}`); + cid = await this.ipfsService.addFolder(repoPath); + this.logger.log(`IPFS CID for ${accountId}: ${cid}`); + } finally { + if (tempFolder) { + try { + await this.tempService.deleteFolder(tempFolder); + } catch (cleanupError) { + this.logger.error( + `Failed to clean up ${tempFolder}: ${cleanupError.message}`, + ); + } + } + } // Store verification result this.logger.log( diff --git a/nest/src/dtos/verify.dto.ts b/nest/src/dtos/verify.dto.ts index 72d765b..3ac63ec 100644 --- a/nest/src/dtos/verify.dto.ts +++ b/nest/src/dtos/verify.dto.ts @@ -1,14 +1,30 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; +import { + IsIn, + IsInt, + IsNotEmpty, + IsOptional, + IsString, + Matches, + Min, +} from 'class-validator'; +import { + NEAR_ACCOUNT_ID_PATTERN, + NETWORK_IDS, + NetworkId, +} from '../constants/validation.constants'; export class VerifyRustDto { @ApiProperty({ description: 'Network ID', example: 'mainnet', + enum: NETWORK_IDS, }) @IsNotEmpty() @IsString() - networkId: string; + @IsIn(NETWORK_IDS) + networkId: NetworkId; @ApiProperty({ description: 'Account ID', @@ -16,6 +32,9 @@ export class VerifyRustDto { }) @IsNotEmpty() @IsString() + @Matches(NEAR_ACCOUNT_ID_PATTERN, { + message: 'accountId must be a valid NEAR account ID', + }) accountId: string; @ApiProperty({ @@ -23,7 +42,9 @@ export class VerifyRustDto { example: 165753012, }) @IsOptional() - @IsNumber() + @Type(() => Number) + @IsInt() + @Min(0) blockId: number; } diff --git a/nest/src/services/compiler/compiler.service.spec.ts b/nest/src/services/compiler/compiler.service.spec.ts index ae8682d..13d578b 100644 --- a/nest/src/services/compiler/compiler.service.spec.ts +++ b/nest/src/services/compiler/compiler.service.spec.ts @@ -13,7 +13,7 @@ describe('CompilerService', () => { { provide: ExecService, useValue: { - executeCommand: jest.fn(), + executeFile: jest.fn(), }, }, ], @@ -26,22 +26,35 @@ describe('CompilerService', () => { it('should verify contract successfully', async () => { const mockOutput = ['Contract verified']; jest - .spyOn(execService, 'executeCommand') + .spyOn(execService, 'executeFile') .mockResolvedValue({ stderr: [], stdout: mockOutput }); const result = await compilerService.verifyContract('test.near', 'mainnet'); expect(result.stdout).toEqual(mockOutput); - expect(execService.executeCommand).toHaveBeenCalledWith( - 'near contract verify deployed-at test.near network-config mainnet now', - ); + expect(execService.executeFile).toHaveBeenCalledWith('near', [ + 'contract', + 'verify', + 'deployed-at', + 'test.near', + 'network-config', + 'mainnet', + 'now', + ]); }); it('should handle errors in contract verification', async () => { const error = new Error('Verification error'); - jest.spyOn(execService, 'executeCommand').mockRejectedValue(error); + jest.spyOn(execService, 'executeFile').mockRejectedValue(error); await expect( compilerService.verifyContract('test.near', 'mainnet'), ).rejects.toThrow(error); }); + + it('should reject unsafe account IDs before executing', async () => { + await expect( + compilerService.verifyContract('test.near; touch /tmp/pwned', 'mainnet'), + ).rejects.toThrow('Invalid NEAR account ID'); + expect(execService.executeFile).not.toHaveBeenCalled(); + }); }); diff --git a/nest/src/services/compiler/compiler.service.ts b/nest/src/services/compiler/compiler.service.ts index 454511b..cb62b19 100644 --- a/nest/src/services/compiler/compiler.service.ts +++ b/nest/src/services/compiler/compiler.service.ts @@ -1,4 +1,8 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { + isValidNearAccountId, + isValidNetworkId, +} from '../../constants/validation.constants'; import { ExecService } from '../exec/exec.service'; @Injectable() @@ -14,10 +18,27 @@ export class CompilerService { accountId: string, networkId: string, ): Promise<{ stdout: string[] }> { - const command = `near contract verify deployed-at ${accountId} network-config ${networkId} now`; - this.logger.log(`Starting contract verification: ${command}`); + if (!isValidNearAccountId(accountId)) { + throw new BadRequestException('Invalid NEAR account ID'); + } + + if (!isValidNetworkId(networkId)) { + throw new BadRequestException('Invalid network ID'); + } + + this.logger.log( + `Starting contract verification for ${accountId} on ${networkId}`, + ); try { - const { stdout } = await this.execService.executeCommand(command); + const { stdout } = await this.execService.executeFile('near', [ + 'contract', + 'verify', + 'deployed-at', + accountId, + 'network-config', + networkId, + 'now', + ]); this.logger.log(`Contract verification completed`); return { stdout }; } catch (error) { diff --git a/nest/src/services/exec/exec.service.spec.ts b/nest/src/services/exec/exec.service.spec.ts index 4910e30..115ed14 100644 --- a/nest/src/services/exec/exec.service.spec.ts +++ b/nest/src/services/exec/exec.service.spec.ts @@ -5,6 +5,7 @@ import { ExecService } from './exec.service'; jest.mock('child_process', () => ({ exec: jest.fn(), + execFile: jest.fn(), })); describe('ExecService', () => { @@ -24,8 +25,8 @@ describe('ExecService', () => { it('should execute command successfully', async () => { const mockExec = cp.exec as unknown as jest.Mock; - mockExec.mockImplementation((cmd, callback) => - callback(null, { stdout: 'success', stderr: '' }), + mockExec.mockImplementation((cmd, options, callback) => + callback(null, 'success', ''), ); await expect(service.executeCommand('echo "Hello World"')).resolves.toEqual( @@ -38,8 +39,8 @@ describe('ExecService', () => { it('should throw ExecException when stderr contains errors', async () => { const mockExec = cp.exec as unknown as jest.Mock; - mockExec.mockImplementation((cmd, callback) => - callback(null, { stdout: '', stderr: 'error: something went wrong' }), + mockExec.mockImplementation((cmd, options, callback) => + callback(null, '', 'error: something went wrong'), ); await expect(service.executeCommand('someCommand')).rejects.toThrow( @@ -49,12 +50,31 @@ describe('ExecService', () => { it('should throw ExecException on command execution failure', async () => { const mockExec = cp.exec as unknown as jest.Mock; - mockExec.mockImplementation((cmd, callback) => - callback(new Error('Execution failed'), { stdout: '', stderr: '' }), + mockExec.mockImplementation((cmd, options, callback) => + callback(new Error('Execution failed'), '', ''), ); await expect(service.executeCommand('faultyCommand')).rejects.toThrow( ExecException, ); }); + + it('should execute a file without a shell', async () => { + const mockExecFile = cp.execFile as unknown as jest.Mock; + mockExecFile.mockImplementation((file, args, options, callback) => + callback(null, 'success', ''), + ); + + await expect(service.executeFile('git', ['status'])).resolves.toEqual({ + stdout: ['success'], + stderr: [], + }); + + expect(mockExecFile).toHaveBeenCalledWith( + 'git', + ['status'], + expect.objectContaining({ shell: false }), + expect.any(Function), + ); + }); }); diff --git a/nest/src/services/exec/exec.service.ts b/nest/src/services/exec/exec.service.ts index 1f67307..5a419c0 100644 --- a/nest/src/services/exec/exec.service.ts +++ b/nest/src/services/exec/exec.service.ts @@ -1,19 +1,27 @@ import { Injectable, Logger } from '@nestjs/common'; import * as cp from 'child_process'; -import { promisify } from 'util'; import { ExecException } from '../../exceptions/exec.exception'; -const execAsync = promisify(cp.exec); +const DEFAULT_COMMAND_TIMEOUT_MS = 15 * 60 * 1000; +const DEFAULT_MAX_BUFFER_BYTES = 20 * 1024 * 1024; @Injectable() export class ExecService { private readonly logger = new Logger(ExecService.name); + private readonly timeoutMs = this.readPositiveInteger( + process.env.EXEC_TIMEOUT_MS, + DEFAULT_COMMAND_TIMEOUT_MS, + ); + private readonly maxBufferBytes = this.readPositiveInteger( + process.env.EXEC_MAX_BUFFER_BYTES, + DEFAULT_MAX_BUFFER_BYTES, + ); async executeCommand( command: string, ): Promise<{ stdout: string[]; stderr: string[] }> { try { - const { stdout, stderr } = await execAsync(command); + const { stdout, stderr } = await this.runShellCommand(command); const { stdout: parsedStdout, stderr: parsedStderr } = this.parseLogs( stdout, @@ -31,13 +39,125 @@ export class ExecService { return { stdout: parsedStdout, stderr: parsedStderr }; } catch (error: any) { - throw new ExecException( + throw this.toExecException(command, error); + } + } + + async executeFile( + file: string, + args: string[], + options: cp.ExecFileOptions = {}, + ): Promise<{ stdout: string[]; stderr: string[] }> { + const command = this.formatCommand(file, args); + + try { + const { stdout, stderr } = await this.runFileCommand(file, args, options); + + const { stdout: parsedStdout, stderr: parsedStderr } = this.parseLogs( + stdout, + stderr, + ); + + if (parsedStderr.length > 0) { + throw new ExecException( + command, + `Error executing command: ${parsedStderr}`, + parsedStdout, + parsedStderr, + ); + } + + return { stdout: parsedStdout, stderr: parsedStderr }; + } catch (error: any) { + throw this.toExecException(command, error); + } + } + + private runShellCommand( + command: string, + ): Promise<{ stdout: string; stderr: string }> { + return new Promise((resolve, reject) => { + cp.exec( command, - error.message, - error.stdout, - error.stderr, + { + timeout: this.timeoutMs, + maxBuffer: this.maxBufferBytes, + }, + (error, stdout, stderr) => { + if (error) { + Object.assign(error, { stdout, stderr }); + reject(error); + return; + } + + resolve({ + stdout: stdout?.toString() ?? '', + stderr: stderr?.toString() ?? '', + }); + }, ); + }); + } + + private runFileCommand( + file: string, + args: string[], + options: cp.ExecFileOptions, + ): Promise<{ stdout: string; stderr: string }> { + return new Promise((resolve, reject) => { + cp.execFile( + file, + args, + { + timeout: this.timeoutMs, + maxBuffer: this.maxBufferBytes, + ...options, + shell: false, + }, + (error, stdout, stderr) => { + if (error) { + Object.assign(error, { stdout, stderr }); + reject(error); + return; + } + + resolve({ + stdout: stdout?.toString() ?? '', + stderr: stderr?.toString() ?? '', + }); + }, + ); + }); + } + + private toExecException(command: string, error: any): ExecException { + if (error instanceof ExecException) { + return error; + } + + return new ExecException( + command, + error.message, + this.normalizeOutput(error.stdout), + this.normalizeOutput(error.stderr), + ); + } + + private normalizeOutput(output?: string | string[]): string[] { + if (Array.isArray(output)) { + return output; } + + return output ? output.split('\n') : []; + } + + private formatCommand(file: string, args: string[]): string { + return [file, ...args].join(' '); + } + + private readPositiveInteger(value: string, fallback: number): number { + const parsed = Number.parseInt(value, 10); + return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback; } private parseLogs( diff --git a/nest/src/services/github/github.service.spec.ts b/nest/src/services/github/github.service.spec.ts index c2b561f..9804baa 100644 --- a/nest/src/services/github/github.service.spec.ts +++ b/nest/src/services/github/github.service.spec.ts @@ -13,7 +13,7 @@ describe('GithubService', () => { { provide: ExecService, useValue: { - executeCommand: jest + executeFile: jest .fn() .mockResolvedValue({ stdout: [], stderr: [] }), }, @@ -30,28 +30,43 @@ describe('GithubService', () => { }); it('should successfully clone a repository', async () => { - await expect(service.clone('sourcePath', 'repo')).resolves.not.toThrow(); - expect(execService.executeCommand).toHaveBeenCalledWith( - 'sh /app/scripts/github/clone.sh sourcePath repo', + await expect( + service.clone('sourcePath', 'https://github.com/user/repo'), + ).resolves.not.toThrow(); + expect(execService.executeFile).toHaveBeenCalledWith( + 'git', + ['clone', '--', 'https://github.com/user/repo'], + { cwd: 'sourcePath' }, ); }); it('should handle errors in clone method', async () => { const error = new Error('Clone failed'); - jest.spyOn(execService, 'executeCommand').mockRejectedValueOnce(error); - await expect(service.clone('sourcePath', 'repo')).rejects.toThrow(error); + jest.spyOn(execService, 'executeFile').mockRejectedValueOnce(error); + await expect( + service.clone('sourcePath', 'https://github.com/user/repo'), + ).rejects.toThrow(error); }); it('should parse source code snapshot', () => { + const sha = '0123456789abcdef0123456789abcdef01234567'; const result = service.parseSourceCodeSnapshot( - 'git+https://github.com/user/repo?rev=abc123', + `git+https://github.com/user/repo?rev=${sha}`, ); expect(result).toEqual({ repoUrl: 'https://github.com/user/repo', - sha: 'abc123', + sha, }); }); + it('should reject non-GitHub source snapshots', () => { + expect(() => + service.parseSourceCodeSnapshot( + 'git+ssh://example.com/user/repo?rev=0123456789abcdef0123456789abcdef01234567', + ), + ).toThrow('Repository URL must use HTTPS'); + }); + it('should get repo path', () => { const result = service.getRepoPath( '/tmp/folder', @@ -61,11 +76,12 @@ describe('GithubService', () => { }); it('should successfully checkout a commit', async () => { - await expect( - service.checkout('/tmp/repo', 'abc123'), - ).resolves.not.toThrow(); - expect(execService.executeCommand).toHaveBeenCalledWith( - 'sh /app/scripts/github/checkout.sh /tmp/repo abc123', + const sha = '0123456789abcdef0123456789abcdef01234567'; + await expect(service.checkout('/tmp/repo', sha)).resolves.not.toThrow(); + expect(execService.executeFile).toHaveBeenCalledWith( + 'git', + ['-c', 'advice.detachedHead=false', 'checkout', '--detach', sha], + { cwd: '/tmp/repo' }, ); }); }); diff --git a/nest/src/services/github/github.service.ts b/nest/src/services/github/github.service.ts index f8b1072..081b7e9 100644 --- a/nest/src/services/github/github.service.ts +++ b/nest/src/services/github/github.service.ts @@ -1,18 +1,21 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import * as path from 'path'; +import { isValidCommitSha } from '../../constants/validation.constants'; import { ExecService } from '../exec/exec.service'; @Injectable() export class GithubService { private readonly logger = new Logger(GithubService.name); - private readonly scriptsPath = '/app/scripts/github'; constructor(private execService: ExecService) {} async clone(sourcePath: string, repo: string): Promise { - const command = `sh ${this.scriptsPath}/clone.sh ${sourcePath} ${repo}`; - this.logger.log(`Starting clone command: ${command}`); + const repoUrl = this.validateRepoUrl(repo); + this.logger.log(`Starting clone command for ${repoUrl}`); try { - await this.execService.executeCommand(command); + await this.execService.executeFile('git', ['clone', '--', repoUrl], { + cwd: sourcePath, + }); this.logger.log(`Repository cloned successfully.`); } catch (error) { this.logger.error(`Error in clone: ${error.message}`); @@ -24,26 +27,91 @@ export class GithubService { repoUrl: string; sha: string; } { - // Remove the 'git+' prefix - const repoInfo = sourceCodeSnapshot.replace('git+', ''); - // Extract the repository URL and the commit hash - const [repoUrl, sha] = repoInfo.split('?rev='); + if (!sourceCodeSnapshot?.startsWith('git+')) { + throw new BadRequestException('Source snapshot must use git+ URL format'); + } + + let snapshotUrl: URL; + + try { + snapshotUrl = new URL(sourceCodeSnapshot.slice('git+'.length)); + } catch { + throw new BadRequestException('Source snapshot must contain a valid URL'); + } + const sha = snapshotUrl.searchParams.get('rev'); + + if (!sha || !isValidCommitSha(sha)) { + throw new BadRequestException( + 'Source snapshot must pin a full 40-character commit SHA', + ); + } + + snapshotUrl.search = ''; + snapshotUrl.hash = ''; + + const repoUrl = this.validateRepoUrl(snapshotUrl.toString()); return { repoUrl, sha }; } getRepoPath(tempFolder: string, repoUrl: string): string { - return `${tempFolder}/${repoUrl.split('/').pop().replace('.git', '')}`; + const parsedUrl = new URL(this.validateRepoUrl(repoUrl)); + const repoName = path.basename(parsedUrl.pathname).replace(/\.git$/, ''); + return path.join(tempFolder, repoName); } async checkout(repoPath: string, sha: string): Promise { - const command = `sh ${this.scriptsPath}/checkout.sh ${repoPath} ${sha}`; - this.logger.log(`Starting checkout command: ${command}`); + if (!isValidCommitSha(sha)) { + throw new BadRequestException('Commit SHA must be 40 hex characters'); + } + + this.logger.log(`Starting checkout command for ${sha}`); try { - await this.execService.executeCommand(command); + await this.execService.executeFile( + 'git', + ['-c', 'advice.detachedHead=false', 'checkout', '--detach', sha], + { cwd: repoPath }, + ); this.logger.log(`Checkout completed successfully.`); } catch (error) { this.logger.error(`Error in checkout: ${error.message}`); throw error; } } + + private validateRepoUrl(repoUrl: string): string { + let parsedUrl: URL; + + try { + parsedUrl = new URL(repoUrl); + } catch { + throw new BadRequestException('Repository URL must be a valid URL'); + } + + if (parsedUrl.protocol !== 'https:') { + throw new BadRequestException('Repository URL must use HTTPS'); + } + + if (parsedUrl.username || parsedUrl.password) { + throw new BadRequestException('Repository URL must not contain secrets'); + } + + if (parsedUrl.hostname.toLowerCase() !== 'github.com') { + throw new BadRequestException('Only github.com repositories are allowed'); + } + + const normalizedPath = parsedUrl.pathname.replace(/\/$/, ''); + if ( + !/^\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?$/.test(normalizedPath) + ) { + throw new BadRequestException( + 'Repository URL must point to a GitHub repo', + ); + } + + parsedUrl.pathname = normalizedPath; + parsedUrl.search = ''; + parsedUrl.hash = ''; + + return parsedUrl.toString(); + } } From f5a0505dbcacbe474cf9d32cfc69f93bfeaffc88 Mon Sep 17 00:00:00 2001 From: Andrey Gruzdev Date: Fri, 29 May 2026 19:14:13 +0200 Subject: [PATCH 2/5] fix(security): align verifier validation with NEAR formats --- .../constants/validation.constants.spec.ts | 51 +++++++++++++++++++ nest/src/constants/validation.constants.ts | 7 +-- .../services/github/github.service.spec.ts | 25 +++++++-- nest/src/services/github/github.service.ts | 10 +++- 4 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 nest/src/constants/validation.constants.spec.ts diff --git a/nest/src/constants/validation.constants.spec.ts b/nest/src/constants/validation.constants.spec.ts new file mode 100644 index 0000000..8b60772 --- /dev/null +++ b/nest/src/constants/validation.constants.spec.ts @@ -0,0 +1,51 @@ +import { isValidCommitSha, isValidNearAccountId } from './validation.constants'; + +describe('validation constants', () => { + it('should accept real NEAR account ID formats', () => { + const validAccountIds = [ + 'sourcescan.near', + 'v2-verifier.sourcescan.near', + 'wrap.near', + 'aurora', + 'dev-123.testnet', + 'b-o_w_e-n', + '98793cd91a3f870fb126f66285808c7e094afcfc4eda8a970f6648cdf0dbd6de', + '0x87b435f1fcb4519306f9b755e274107cc78ac4e3', + '0s87b435f1fcb4519306f9b755e274107cc78ac4e3', + ]; + + for (const accountId of validAccountIds) { + expect(isValidNearAccountId(accountId)).toBe(true); + } + }); + + it('should reject invalid NEAR account IDs', () => { + const invalidAccountIds = [ + '', + 'a', + 'SourceScan.near', + '-near', + 'near-', + '.near', + 'near.', + 'a..near', + '0__0', + '0_-_0', + 'hello world', + 'near;touch', + 'abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz', + ]; + + for (const accountId of invalidAccountIds) { + expect(isValidNearAccountId(accountId)).toBe(false); + } + }); + + it('should validate full commit SHAs only', () => { + expect(isValidCommitSha('0123456789abcdef0123456789abcdef01234567')).toBe( + true, + ); + expect(isValidCommitSha('a80bc29')).toBe(false); + expect(isValidCommitSha('main')).toBe(false); + }); +}); diff --git a/nest/src/constants/validation.constants.ts b/nest/src/constants/validation.constants.ts index 79efb8a..fdbd52d 100644 --- a/nest/src/constants/validation.constants.ts +++ b/nest/src/constants/validation.constants.ts @@ -5,12 +5,9 @@ export type NetworkId = (typeof NETWORK_IDS)[number]; export const COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i; const NAMED_ACCOUNT_PATTERN = - /^(?=.{2,64}$)(?:[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?))*$/; -const IMPLICIT_ACCOUNT_PATTERN = /^[0-9a-f]{64}$/; + /^(?=.{2,64}$)(?:(?:[a-z0-9]+[-_])*[a-z0-9]+\.)*(?:[a-z0-9]+[-_])*[a-z0-9]+$/; -export const NEAR_ACCOUNT_ID_PATTERN = new RegExp( - `${NAMED_ACCOUNT_PATTERN.source}|${IMPLICIT_ACCOUNT_PATTERN.source}`, -); +export const NEAR_ACCOUNT_ID_PATTERN = NAMED_ACCOUNT_PATTERN; export function isValidNetworkId(networkId: string): networkId is NetworkId { return NETWORK_IDS.includes(networkId as NetworkId); diff --git a/nest/src/services/github/github.service.spec.ts b/nest/src/services/github/github.service.spec.ts index 9804baa..0484666 100644 --- a/nest/src/services/github/github.service.spec.ts +++ b/nest/src/services/github/github.service.spec.ts @@ -49,16 +49,35 @@ describe('GithubService', () => { }); it('should parse source code snapshot', () => { - const sha = '0123456789abcdef0123456789abcdef01234567'; + const sha = '9c16aaff3c0fe5bda4d8ffb418c4bb2b535eb420'; const result = service.parseSourceCodeSnapshot( - `git+https://github.com/user/repo?rev=${sha}`, + `git+https://github.com/near/cargo-near-new-project-template?rev=${sha}`, ); expect(result).toEqual({ - repoUrl: 'https://github.com/user/repo', + repoUrl: 'https://github.com/near/cargo-near-new-project-template', sha, }); }); + it('should parse NEP-330 source code snapshot with fragment SHA', () => { + const sha = '9c16aaff3c0fe5bda4d8ffb418c4bb2b535eb420'; + const result = service.parseSourceCodeSnapshot( + `git+https://github.com/near/cargo-near-new-project-template.git#${sha}`, + ); + expect(result).toEqual({ + repoUrl: 'https://github.com/near/cargo-near-new-project-template.git', + sha, + }); + }); + + it('should reject source snapshots with conflicting SHAs', () => { + expect(() => + service.parseSourceCodeSnapshot( + 'git+https://github.com/user/repo?rev=0123456789abcdef0123456789abcdef01234567#89abcdef0123456789abcdef0123456789abcdef', + ), + ).toThrow('Source snapshot must not contain conflicting commit SHAs'); + }); + it('should reject non-GitHub source snapshots', () => { expect(() => service.parseSourceCodeSnapshot( diff --git a/nest/src/services/github/github.service.ts b/nest/src/services/github/github.service.ts index 081b7e9..42cefdf 100644 --- a/nest/src/services/github/github.service.ts +++ b/nest/src/services/github/github.service.ts @@ -38,7 +38,15 @@ export class GithubService { } catch { throw new BadRequestException('Source snapshot must contain a valid URL'); } - const sha = snapshotUrl.searchParams.get('rev'); + const revSha = snapshotUrl.searchParams.get('rev'); + const hashSha = snapshotUrl.hash ? snapshotUrl.hash.slice(1) : null; + if (revSha && hashSha && revSha !== hashSha) { + throw new BadRequestException( + 'Source snapshot must not contain conflicting commit SHAs', + ); + } + + const sha = revSha ?? hashSha; if (!sha || !isValidCommitSha(sha)) { throw new BadRequestException( From 6d158ddbb54da6e7748b4f8f29f2256d0a2631fb Mon Sep 17 00:00:00 2001 From: Andrey Gruzdev Date: Fri, 29 May 2026 19:20:12 +0200 Subject: [PATCH 3/5] fix(security): extend verifier build timeout --- .../compiler/compiler.service.spec.ts | 33 ++++++++++++++----- .../src/services/compiler/compiler.service.ts | 33 ++++++++++++++----- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/nest/src/services/compiler/compiler.service.spec.ts b/nest/src/services/compiler/compiler.service.spec.ts index 13d578b..90b1085 100644 --- a/nest/src/services/compiler/compiler.service.spec.ts +++ b/nest/src/services/compiler/compiler.service.spec.ts @@ -5,8 +5,11 @@ import { CompilerService } from './compiler.service'; describe('CompilerService', () => { let compilerService: CompilerService; let execService: ExecService; + const originalCompilerTimeoutMs = process.env.COMPILER_TIMEOUT_MS; beforeEach(async () => { + delete process.env.COMPILER_TIMEOUT_MS; + const moduleRef: TestingModule = await Test.createTestingModule({ providers: [ CompilerService, @@ -23,6 +26,14 @@ describe('CompilerService', () => { execService = moduleRef.get(ExecService); }); + afterAll(() => { + if (originalCompilerTimeoutMs) { + process.env.COMPILER_TIMEOUT_MS = originalCompilerTimeoutMs; + } else { + delete process.env.COMPILER_TIMEOUT_MS; + } + }); + it('should verify contract successfully', async () => { const mockOutput = ['Contract verified']; jest @@ -31,15 +42,19 @@ describe('CompilerService', () => { const result = await compilerService.verifyContract('test.near', 'mainnet'); expect(result.stdout).toEqual(mockOutput); - expect(execService.executeFile).toHaveBeenCalledWith('near', [ - 'contract', - 'verify', - 'deployed-at', - 'test.near', - 'network-config', - 'mainnet', - 'now', - ]); + expect(execService.executeFile).toHaveBeenCalledWith( + 'near', + [ + 'contract', + 'verify', + 'deployed-at', + 'test.near', + 'network-config', + 'mainnet', + 'now', + ], + { timeout: 60 * 60 * 1000 }, + ); }); it('should handle errors in contract verification', async () => { diff --git a/nest/src/services/compiler/compiler.service.ts b/nest/src/services/compiler/compiler.service.ts index cb62b19..f3dbd03 100644 --- a/nest/src/services/compiler/compiler.service.ts +++ b/nest/src/services/compiler/compiler.service.ts @@ -5,9 +5,15 @@ import { } from '../../constants/validation.constants'; import { ExecService } from '../exec/exec.service'; +const DEFAULT_COMPILER_TIMEOUT_MS = 60 * 60 * 1000; + @Injectable() export class CompilerService { private readonly logger = new Logger(CompilerService.name); + private readonly compilerTimeoutMs = this.readPositiveInteger( + process.env.COMPILER_TIMEOUT_MS, + DEFAULT_COMPILER_TIMEOUT_MS, + ); constructor(private execService: ExecService) {} @@ -30,15 +36,19 @@ export class CompilerService { `Starting contract verification for ${accountId} on ${networkId}`, ); try { - const { stdout } = await this.execService.executeFile('near', [ - 'contract', - 'verify', - 'deployed-at', - accountId, - 'network-config', - networkId, - 'now', - ]); + const { stdout } = await this.execService.executeFile( + 'near', + [ + 'contract', + 'verify', + 'deployed-at', + accountId, + 'network-config', + networkId, + 'now', + ], + { timeout: this.compilerTimeoutMs }, + ); this.logger.log(`Contract verification completed`); return { stdout }; } catch (error) { @@ -46,4 +56,9 @@ export class CompilerService { throw error; } } + + private readPositiveInteger(value: string, fallback: number): number { + const parsed = Number.parseInt(value, 10); + return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback; + } } From c51bfad67db8eaa75d3eab0bda70605828c81ef3 Mon Sep 17 00:00:00 2001 From: Andrey Gruzdev Date: Fri, 29 May 2026 19:30:45 +0200 Subject: [PATCH 4/5] fix(security): preserve git snapshot compatibility --- nest/package-lock.json | 842 +++++++++--------- nest/src/constants/validation.constants.ts | 6 + .../compiler/compiler.service.spec.ts | 33 +- .../src/services/compiler/compiler.service.ts | 33 +- .../services/github/github.service.spec.ts | 63 +- nest/src/services/github/github.service.ts | 147 ++- 6 files changed, 608 insertions(+), 516 deletions(-) diff --git a/nest/package-lock.json b/nest/package-lock.json index 1cee524..d4e0f84 100644 --- a/nest/package-lock.json +++ b/nest/package-lock.json @@ -43,16 +43,16 @@ } }, "node_modules/@angular-devkit/core": { - "version": "19.2.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.24.tgz", - "integrity": "sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==", + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "8.18.0", + "ajv": "8.17.1", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", - "picomatch": "4.0.4", + "picomatch": "4.0.2", "rxjs": "7.8.1", "source-map": "0.7.4" }, @@ -81,13 +81,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "19.2.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.24.tgz", - "integrity": "sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==", + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", + "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.24", + "@angular-devkit/core": "19.2.19", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "5.4.1", @@ -100,14 +100,14 @@ } }, "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.24", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.24.tgz", - "integrity": "sha512-bsStZQG67J1HBqTmWxtIcobvgrn32L4UOdL7hGyOru5VxDWPNA8pRnDYavT3hnJeBkJYPoQIw8u7Dm0ecoQprw==", + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", + "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.24", - "@angular-devkit/schematics": "19.2.24", + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", "@inquirer/prompts": "7.3.2", "ansi-colors": "4.1.3", "symbol-observable": "4.0.0", @@ -679,9 +679,9 @@ "license": "MIT" }, "node_modules/@borewit/text-codec": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", - "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", + "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", "license": "MIT", "funding": { "type": "github", @@ -841,9 +841,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1303,6 +1303,27 @@ } } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1744,9 +1765,9 @@ } }, "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1782,13 +1803,13 @@ "license": "ISC" }, "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2204,15 +2225,15 @@ } }, "node_modules/@nestjs/cli": { - "version": "11.0.21", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.21.tgz", - "integrity": "sha512-F8mV0Sj/zVEouzR3NxBuJy08YHTUOmC5Xdcx3qIIaJWzrm8Vw86CHkhkaPBJ5ewRMHPDCShPmhsfwhpCcjts3A==", + "version": "11.0.14", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.14.tgz", + "integrity": "sha512-YwP03zb5VETTwelXU+AIzMVbEZKk/uxJL+z9pw0mdG9ogAtqZ6/mpmIM4nEq/NU8D0a7CBRLcMYUmWW/55pfqw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.24", - "@angular-devkit/schematics": "19.2.24", - "@angular-devkit/schematics-cli": "19.2.24", + "@angular-devkit/core": "19.2.19", + "@angular-devkit/schematics": "19.2.19", + "@angular-devkit/schematics-cli": "19.2.19", "@inquirer/prompts": "7.10.1", "@nestjs/schematics": "^11.0.1", "ansis": "4.2.0", @@ -2220,13 +2241,13 @@ "cli-table3": "0.6.5", "commander": "4.1.1", "fork-ts-checker-webpack-plugin": "9.1.0", - "glob": "13.0.6", + "glob": "13.0.0", "node-emoji": "1.11.0", "ora": "5.4.1", "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.2.0", "typescript": "5.9.3", - "webpack": "5.106.0", + "webpack": "5.103.0", "webpack-node-externals": "3.0.0" }, "bin": { @@ -2236,7 +2257,7 @@ "node": ">= 20.11" }, "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0", + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", "@swc/core": "^1.3.62" }, "peerDependenciesMeta": { @@ -2249,12 +2270,12 @@ } }, "node_modules/@nestjs/common": { - "version": "11.1.24", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.24.tgz", - "integrity": "sha512-9zHxaDDM+oXW9As6UsP5yYB+UqczBmpeSCIFWdPEtEukMnZhxODG1BBjaUcdBB8Sc1uzojSJSJlp3yFp853t1g==", + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.9.tgz", + "integrity": "sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==", "license": "MIT", "dependencies": { - "file-type": "21.3.4", + "file-type": "21.1.0", "iterare": "1.2.1", "load-esm": "1.0.3", "tslib": "2.8.1", @@ -2280,16 +2301,16 @@ } }, "node_modules/@nestjs/core": { - "version": "11.1.24", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.24.tgz", - "integrity": "sha512-K4bzT+lEdd0Hhcsw3jtk56QAW6s6skK3ViN7hIROSN0kUf4ROwWEAKopJID6yhPQxB45kDtP2wEcjzE8171J3g==", + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.9.tgz", + "integrity": "sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==", "hasInstallScript": true, "license": "MIT", "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", - "path-to-regexp": "8.4.2", + "path-to-regexp": "8.3.0", "tslib": "2.8.1", "uid": "2.0.2" }, @@ -2321,14 +2342,14 @@ } }, "node_modules/@nestjs/mapped-types": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.1.tgz", - "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", + "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", "license": "MIT", "peerDependencies": { "@nestjs/common": "^10.0.0 || ^11.0.0", "class-transformer": "^0.4.0 || ^0.5.0", - "class-validator": "^0.13.0 || ^0.14.0 || ^0.15.0", + "class-validator": "^0.13.0 || ^0.14.0", "reflect-metadata": "^0.1.12 || ^0.2.0" }, "peerDependenciesMeta": { @@ -2341,15 +2362,15 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "11.1.24", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.24.tgz", - "integrity": "sha512-CeMKbRBm05aOBiWhIHWO2xDeHbxynBF9ySQv3gRjObz2N5+uJnYriAYkHvVqvC4JIydmMPmT5VdICFNlNz3qyA==", + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.9.tgz", + "integrity": "sha512-GVd3+0lO0mJq2m1kl9hDDnVrX3Nd4oH3oDfklz0pZEVEVS0KVSp63ufHq2Lu9cyPdSBuelJr9iPm2QQ1yX+Kmw==", "license": "MIT", "dependencies": { - "cors": "2.8.6", - "express": "5.2.1", - "multer": "2.1.1", - "path-to-regexp": "8.4.2", + "cors": "2.8.5", + "express": "5.1.0", + "multer": "2.0.2", + "path-to-regexp": "8.3.0", "tslib": "2.8.1" }, "funding": { @@ -2375,43 +2396,94 @@ } }, "node_modules/@nestjs/schematics": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.1.0.tgz", - "integrity": "sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==", + "version": "11.0.9", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", + "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.24", - "@angular-devkit/schematics": "19.2.24", - "comment-json": "5.0.0", + "@angular-devkit/core": "19.2.17", + "@angular-devkit/schematics": "19.2.17", + "comment-json": "4.4.1", "jsonc-parser": "3.3.1", "pluralize": "8.0.0" }, "peerDependencies": { - "prettier": "^3.0.0", "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", + "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" }, "peerDependenciesMeta": { - "prettier": { + "chokidar": { "optional": true } } }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", + "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.17", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@nestjs/swagger": { - "version": "11.4.4", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.4.4.tgz", - "integrity": "sha512-VaIo1ruV2G7b+f2zPzkBSUNy9a/WQ9sg8TLKhWlrTfg4O6U10M/PA7Xi6XMXadOVhwOqoesijba8jH3i/3adrA==", + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.3.tgz", + "integrity": "sha512-a0xFfjeqk69uHIUpP8u0ryn4cKuHdra2Ug96L858i0N200Hxho+n3j+TlQXyOF4EstLSGjTfxI1Xb2E1lUxeNg==", "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.16.0", - "@nestjs/mapped-types": "2.1.1", + "@nestjs/mapped-types": "2.1.0", "js-yaml": "4.1.1", - "lodash": "4.18.1", - "path-to-regexp": "8.4.2", - "swagger-ui-dist": "5.32.6" + "lodash": "4.17.21", + "path-to-regexp": "8.3.0", + "swagger-ui-dist": "5.30.2" }, "peerDependencies": { - "@fastify/static": "^8.0.0 || ^9.0.0", + "@fastify/static": "^8.0.0", "@nestjs/common": "^11.0.1", "@nestjs/core": "^11.0.1", "class-transformer": "*", @@ -2569,13 +2641,14 @@ } }, "node_modules/@tokenizer/inflate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", - "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.3.1.tgz", + "integrity": "sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==", "license": "MIT", "dependencies": { - "debug": "^4.4.3", - "token-types": "^6.1.1" + "debug": "^4.4.1", + "fflate": "^0.8.2", + "token-types": "^6.0.0" }, "engines": { "node": ">=18" @@ -3063,9 +3136,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3073,13 +3146,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -3595,9 +3668,9 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -3630,22 +3703,10 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { @@ -3767,9 +3828,9 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -3805,15 +3866,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", - "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" } }, "node_modules/babel-jest": { @@ -3944,16 +4004,13 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", - "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "version": "2.8.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.32.tgz", + "integrity": "sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" + "baseline-browser-mapping": "dist/cli.js" } }, "node_modules/bl": { @@ -3969,15 +4026,15 @@ } }, "node_modules/bn.js": { - "version": "4.12.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", - "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", + "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", @@ -3986,7 +4043,7 @@ "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", - "qs": "^6.14.1", + "qs": "^6.14.0", "raw-body": "^3.0.1", "type-is": "^2.0.1" }, @@ -4005,9 +4062,9 @@ "license": "Apache-2.0" }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -4035,9 +4092,9 @@ "license": "MIT" }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", + "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", "dev": true, "funding": [ { @@ -4055,11 +4112,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.8.25", + "caniuse-lite": "^1.0.30001754", + "electron-to-chromium": "^1.5.249", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -4192,9 +4249,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", "dev": true, "funding": [ { @@ -4468,13 +4525,14 @@ } }, "node_modules/comment-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", - "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", "dev": true, "license": "MIT", "dependencies": { "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", "esprima": "^4.0.1" }, "engines": { @@ -4513,9 +4571,9 @@ } }, "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", "engines": { "node": ">=18" @@ -4559,10 +4617,17 @@ "node": ">=6.6.0" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "license": "MIT", "dependencies": { "object-assign": "^4", @@ -4570,10 +4635,6 @@ }, "engines": { "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/cosmiconfig": { @@ -4749,9 +4810,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.364", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", - "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", + "version": "1.5.262", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.262.tgz", + "integrity": "sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==", "dev": true, "license": "ISC" }, @@ -4800,14 +4861,14 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.22.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz", - "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" + "tapable": "^2.2.0" }, "engines": { "node": ">=10.13.0" @@ -4842,9 +4903,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -5042,9 +5103,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -5228,19 +5289,18 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", "dependencies": { "accepts": "^2.0.0", - "body-parser": "^2.2.1", + "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", - "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", @@ -5305,9 +5365,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { @@ -5349,6 +5409,12 @@ } } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -5363,14 +5429,14 @@ } }, "node_modules/file-type": { - "version": "21.3.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", - "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "version": "21.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.1.0.tgz", + "integrity": "sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==", "license": "MIT", "dependencies": { - "@tokenizer/inflate": "^0.4.1", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", + "@tokenizer/inflate": "^0.3.1", + "strtok3": "^10.3.1", + "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" }, "engines": { @@ -5446,16 +5512,16 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", @@ -5724,17 +5790,17 @@ } }, "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", "license": "BlueOak-1.0.0", "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5760,37 +5826,16 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/glob/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/glob/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5836,9 +5881,9 @@ "license": "MIT" }, "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5973,19 +6018,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6506,9 +6538,9 @@ } }, "node_modules/jest-config/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6544,13 +6576,13 @@ "license": "ISC" }, "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6863,9 +6895,9 @@ } }, "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6901,13 +6933,13 @@ "license": "ISC" }, "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -7267,9 +7299,9 @@ } }, "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, "node_modules/lodash.memoize": { @@ -7436,9 +7468,9 @@ } }, "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { @@ -7496,9 +7528,9 @@ "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", "dependencies": { @@ -7512,21 +7544,32 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7534,22 +7577,21 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", - "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", - "type-is": "^1.6.18" + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" }, "engines": { "node": ">= 10.16.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/multer/node_modules/media-typer": { @@ -7818,14 +7860,11 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", - "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -8070,34 +8109,34 @@ } }, "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.5.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", - "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" } }, "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "funding": { "type": "opencollective", @@ -8122,9 +8161,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", "engines": { @@ -8304,13 +8343,10 @@ } }, "node_modules/proxy-from-env": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" }, "node_modules/punycode": { "version": "2.3.1", @@ -8340,9 +8376,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -8354,6 +8390,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -8583,9 +8629,9 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { @@ -8645,35 +8691,41 @@ } }, "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", "dependencies": { - "debug": "^4.4.3", + "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "statuses": "^2.0.2" + "statuses": "^2.0.1" }, "engines": { "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" } }, "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", @@ -8683,10 +8735,6 @@ }, "engines": { "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -8738,13 +8786,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" @@ -9006,9 +9054,9 @@ } }, "node_modules/strtok3": { - "version": "10.3.5", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", - "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", "license": "MIT", "dependencies": { "@tokenizer/token": "^0.3.0" @@ -9035,9 +9083,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.32.6", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.32.6.tgz", - "integrity": "sha512-75ttZNaYCLoFPnozPZcTUU6mS3wKT8l7WLjU5zJSHFeJa23i5vtnze6IiCl4jDMPeQTXVXIgovq4M11NNfQvSA==", + "version": "5.30.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz", + "integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" @@ -9070,9 +9118,9 @@ } }, "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -9084,9 +9132,9 @@ } }, "node_modules/terser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", - "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -9103,15 +9151,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", - "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "engines": { @@ -9125,39 +9174,12 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { - "@minify-html/node": { - "optional": true - }, "@swc/core": { "optional": true }, - "@swc/css": { - "optional": true - }, - "@swc/html": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "cssnano": { - "optional": true - }, - "csso": { - "optional": true - }, "esbuild": { "optional": true }, - "html-minifier-terser": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "postcss": { - "optional": true - }, "uglify-js": { "optional": true } @@ -9314,6 +9336,19 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -9344,12 +9379,12 @@ } }, "node_modules/token-types": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", - "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", + "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", "license": "MIT", "dependencies": { - "@borewit/text-codec": "^0.2.1", + "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, @@ -9530,34 +9565,17 @@ } }, "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "content-type": "^2.0.0", + "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.6" } }, "node_modules/typedarray": { @@ -9704,9 +9722,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "dev": true, "funding": [ { @@ -9794,9 +9812,9 @@ } }, "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "dev": true, "license": "MIT", "dependencies": { @@ -9824,9 +9842,9 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.106.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.0.tgz", - "integrity": "sha512-Pkx5joZ9RrdgO5LBkyX1L2ZAJeK/Taz3vqZ9CbcP0wS5LEMx5QkKsEwLl29QJfihZ+DKRBFldzy1O30pJ1MDpA==", + "version": "5.103.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", + "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", "dev": true, "license": "MIT", "dependencies": { @@ -9836,12 +9854,12 @@ "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", + "acorn": "^8.15.0", "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", + "browserslist": "^4.26.3", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -9852,9 +9870,9 @@ "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -9883,9 +9901,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", - "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, "license": "MIT", "engines": { diff --git a/nest/src/constants/validation.constants.ts b/nest/src/constants/validation.constants.ts index fdbd52d..7388b0a 100644 --- a/nest/src/constants/validation.constants.ts +++ b/nest/src/constants/validation.constants.ts @@ -3,6 +3,8 @@ export const NETWORK_IDS = ['mainnet', 'testnet'] as const; export type NetworkId = (typeof NETWORK_IDS)[number]; export const COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i; +export const GIT_REF_PATTERN = + /^(?!-)(?!.*(?:\.\.|@\{|\/\/|\\))[A-Za-z0-9._/@+-]{1,256}$/; const NAMED_ACCOUNT_PATTERN = /^(?=.{2,64}$)(?:(?:[a-z0-9]+[-_])*[a-z0-9]+\.)*(?:[a-z0-9]+[-_])*[a-z0-9]+$/; @@ -17,6 +19,10 @@ export function isValidCommitSha(sha: string): boolean { return COMMIT_SHA_PATTERN.test(sha); } +export function isValidGitRef(ref: string): boolean { + return GIT_REF_PATTERN.test(ref); +} + export function isValidNearAccountId(accountId: string): boolean { return NEAR_ACCOUNT_ID_PATTERN.test(accountId); } diff --git a/nest/src/services/compiler/compiler.service.spec.ts b/nest/src/services/compiler/compiler.service.spec.ts index 90b1085..13d578b 100644 --- a/nest/src/services/compiler/compiler.service.spec.ts +++ b/nest/src/services/compiler/compiler.service.spec.ts @@ -5,11 +5,8 @@ import { CompilerService } from './compiler.service'; describe('CompilerService', () => { let compilerService: CompilerService; let execService: ExecService; - const originalCompilerTimeoutMs = process.env.COMPILER_TIMEOUT_MS; beforeEach(async () => { - delete process.env.COMPILER_TIMEOUT_MS; - const moduleRef: TestingModule = await Test.createTestingModule({ providers: [ CompilerService, @@ -26,14 +23,6 @@ describe('CompilerService', () => { execService = moduleRef.get(ExecService); }); - afterAll(() => { - if (originalCompilerTimeoutMs) { - process.env.COMPILER_TIMEOUT_MS = originalCompilerTimeoutMs; - } else { - delete process.env.COMPILER_TIMEOUT_MS; - } - }); - it('should verify contract successfully', async () => { const mockOutput = ['Contract verified']; jest @@ -42,19 +31,15 @@ describe('CompilerService', () => { const result = await compilerService.verifyContract('test.near', 'mainnet'); expect(result.stdout).toEqual(mockOutput); - expect(execService.executeFile).toHaveBeenCalledWith( - 'near', - [ - 'contract', - 'verify', - 'deployed-at', - 'test.near', - 'network-config', - 'mainnet', - 'now', - ], - { timeout: 60 * 60 * 1000 }, - ); + expect(execService.executeFile).toHaveBeenCalledWith('near', [ + 'contract', + 'verify', + 'deployed-at', + 'test.near', + 'network-config', + 'mainnet', + 'now', + ]); }); it('should handle errors in contract verification', async () => { diff --git a/nest/src/services/compiler/compiler.service.ts b/nest/src/services/compiler/compiler.service.ts index f3dbd03..cb62b19 100644 --- a/nest/src/services/compiler/compiler.service.ts +++ b/nest/src/services/compiler/compiler.service.ts @@ -5,15 +5,9 @@ import { } from '../../constants/validation.constants'; import { ExecService } from '../exec/exec.service'; -const DEFAULT_COMPILER_TIMEOUT_MS = 60 * 60 * 1000; - @Injectable() export class CompilerService { private readonly logger = new Logger(CompilerService.name); - private readonly compilerTimeoutMs = this.readPositiveInteger( - process.env.COMPILER_TIMEOUT_MS, - DEFAULT_COMPILER_TIMEOUT_MS, - ); constructor(private execService: ExecService) {} @@ -36,19 +30,15 @@ export class CompilerService { `Starting contract verification for ${accountId} on ${networkId}`, ); try { - const { stdout } = await this.execService.executeFile( - 'near', - [ - 'contract', - 'verify', - 'deployed-at', - accountId, - 'network-config', - networkId, - 'now', - ], - { timeout: this.compilerTimeoutMs }, - ); + const { stdout } = await this.execService.executeFile('near', [ + 'contract', + 'verify', + 'deployed-at', + accountId, + 'network-config', + networkId, + 'now', + ]); this.logger.log(`Contract verification completed`); return { stdout }; } catch (error) { @@ -56,9 +46,4 @@ export class CompilerService { throw error; } } - - private readPositiveInteger(value: string, fallback: number): number { - const parsed = Number.parseInt(value, 10); - return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : fallback; - } } diff --git a/nest/src/services/github/github.service.spec.ts b/nest/src/services/github/github.service.spec.ts index 0484666..04f4f93 100644 --- a/nest/src/services/github/github.service.spec.ts +++ b/nest/src/services/github/github.service.spec.ts @@ -49,41 +49,80 @@ describe('GithubService', () => { }); it('should parse source code snapshot', () => { - const sha = '9c16aaff3c0fe5bda4d8ffb418c4bb2b535eb420'; + const ref = '9c16aaff3c0fe5bda4d8ffb418c4bb2b535eb420'; const result = service.parseSourceCodeSnapshot( - `git+https://github.com/near/cargo-near-new-project-template?rev=${sha}`, + `git+https://github.com/near/cargo-near-new-project-template?rev=${ref}`, ); expect(result).toEqual({ repoUrl: 'https://github.com/near/cargo-near-new-project-template', - sha, + sha: ref, }); }); it('should parse NEP-330 source code snapshot with fragment SHA', () => { - const sha = '9c16aaff3c0fe5bda4d8ffb418c4bb2b535eb420'; + const ref = '9c16aaff3c0fe5bda4d8ffb418c4bb2b535eb420'; const result = service.parseSourceCodeSnapshot( - `git+https://github.com/near/cargo-near-new-project-template.git#${sha}`, + `git+https://github.com/near/cargo-near-new-project-template.git#${ref}`, ); expect(result).toEqual({ repoUrl: 'https://github.com/near/cargo-near-new-project-template.git', - sha, + sha: ref, }); }); - it('should reject source snapshots with conflicting SHAs', () => { + it('should parse non-GitHub and symbolic git refs', () => { + const result = service.parseSourceCodeSnapshot( + 'git+https://gitlab.com/user/repo.git?rev=v1.2.3', + ); + + expect(result).toEqual({ + repoUrl: 'https://gitlab.com/user/repo.git', + sha: 'v1.2.3', + }); + }); + + it('should parse SSH source snapshots', () => { + const result = service.parseSourceCodeSnapshot( + 'git+ssh://git@gitlab.com/user/repo.git?rev=feature/repro-build', + ); + + expect(result).toEqual({ + repoUrl: 'ssh://git@gitlab.com/user/repo.git', + sha: 'feature/repro-build', + }); + }); + + it('should parse SCP-like SSH source snapshots', () => { + const result = service.parseSourceCodeSnapshot( + 'git+git@gitlab.com:user/repo.git?rev=a80bc29', + ); + + expect(result).toEqual({ + repoUrl: 'git@gitlab.com:user/repo.git', + sha: 'a80bc29', + }); + }); + + it('should reject source snapshots with conflicting refs', () => { expect(() => service.parseSourceCodeSnapshot( 'git+https://github.com/user/repo?rev=0123456789abcdef0123456789abcdef01234567#89abcdef0123456789abcdef0123456789abcdef', ), - ).toThrow('Source snapshot must not contain conflicting commit SHAs'); + ).toThrow('Source snapshot must not contain conflicting git refs'); }); - it('should reject non-GitHub source snapshots', () => { + it('should reject unsafe refs', () => { expect(() => service.parseSourceCodeSnapshot( - 'git+ssh://example.com/user/repo?rev=0123456789abcdef0123456789abcdef01234567', + 'git+https://github.com/user/repo?rev=main;touch', ), - ).toThrow('Repository URL must use HTTPS'); + ).toThrow('Source snapshot must pin a safe git ref'); + }); + + it('should reject local filesystem source snapshots', () => { + expect(() => + service.parseSourceCodeSnapshot('git+file:///tmp/repo?rev=main'), + ).toThrow('Repository URL must use HTTPS, SSH, or git protocol'); }); it('should get repo path', () => { @@ -99,7 +138,7 @@ describe('GithubService', () => { await expect(service.checkout('/tmp/repo', sha)).resolves.not.toThrow(); expect(execService.executeFile).toHaveBeenCalledWith( 'git', - ['-c', 'advice.detachedHead=false', 'checkout', '--detach', sha], + ['-c', 'advice.detachedHead=false', 'checkout', '--detach', '--', sha], { cwd: '/tmp/repo' }, ); }); diff --git a/nest/src/services/github/github.service.ts b/nest/src/services/github/github.service.ts index 42cefdf..a7b6279 100644 --- a/nest/src/services/github/github.service.ts +++ b/nest/src/services/github/github.service.ts @@ -1,8 +1,11 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common'; import * as path from 'path'; -import { isValidCommitSha } from '../../constants/validation.constants'; +import { isValidGitRef } from '../../constants/validation.constants'; import { ExecService } from '../exec/exec.service'; +const SCP_LIKE_GIT_URL_PATTERN = + /^[A-Za-z0-9._-]+@[A-Za-z0-9.-]+:[A-Za-z0-9._~/-]+(?:\.git)?$/; + @Injectable() export class GithubService { private readonly logger = new Logger(GithubService.name); @@ -31,52 +34,35 @@ export class GithubService { throw new BadRequestException('Source snapshot must use git+ URL format'); } - let snapshotUrl: URL; - - try { - snapshotUrl = new URL(sourceCodeSnapshot.slice('git+'.length)); - } catch { - throw new BadRequestException('Source snapshot must contain a valid URL'); - } - const revSha = snapshotUrl.searchParams.get('rev'); - const hashSha = snapshotUrl.hash ? snapshotUrl.hash.slice(1) : null; - if (revSha && hashSha && revSha !== hashSha) { - throw new BadRequestException( - 'Source snapshot must not contain conflicting commit SHAs', - ); - } - - const sha = revSha ?? hashSha; + const { repoUrl: rawRepoUrl, ref } = this.parseGitSnapshot( + sourceCodeSnapshot.slice('git+'.length), + ); - if (!sha || !isValidCommitSha(sha)) { - throw new BadRequestException( - 'Source snapshot must pin a full 40-character commit SHA', - ); + if (!ref || !isValidGitRef(ref)) { + throw new BadRequestException('Source snapshot must pin a safe git ref'); } - snapshotUrl.search = ''; - snapshotUrl.hash = ''; - - const repoUrl = this.validateRepoUrl(snapshotUrl.toString()); - return { repoUrl, sha }; + const repoUrl = this.validateRepoUrl(rawRepoUrl); + return { repoUrl, sha: ref }; } getRepoPath(tempFolder: string, repoUrl: string): string { - const parsedUrl = new URL(this.validateRepoUrl(repoUrl)); - const repoName = path.basename(parsedUrl.pathname).replace(/\.git$/, ''); + const normalizedRepoUrl = this.validateRepoUrl(repoUrl); + const repoPath = this.getRepoUrlPath(normalizedRepoUrl); + const repoName = path.basename(repoPath).replace(/\.git$/, ''); return path.join(tempFolder, repoName); } - async checkout(repoPath: string, sha: string): Promise { - if (!isValidCommitSha(sha)) { - throw new BadRequestException('Commit SHA must be 40 hex characters'); + async checkout(repoPath: string, ref: string): Promise { + if (!isValidGitRef(ref)) { + throw new BadRequestException('Git ref contains unsafe characters'); } - this.logger.log(`Starting checkout command for ${sha}`); + this.logger.log(`Starting checkout command for ${ref}`); try { await this.execService.executeFile( 'git', - ['-c', 'advice.detachedHead=false', 'checkout', '--detach', sha], + ['-c', 'advice.detachedHead=false', 'checkout', '--detach', '--', ref], { cwd: repoPath }, ); this.logger.log(`Checkout completed successfully.`); @@ -86,7 +72,73 @@ export class GithubService { } } + private parseGitSnapshot(snapshot: string): { + repoUrl: string; + ref: string | null; + } { + try { + const snapshotUrl = new URL(snapshot); + const revRef = snapshotUrl.searchParams.get('rev'); + const hashRef = snapshotUrl.hash + ? decodeURIComponent(snapshotUrl.hash.slice(1)) + : null; + + if (revRef && hashRef && revRef !== hashRef) { + throw new BadRequestException( + 'Source snapshot must not contain conflicting git refs', + ); + } + + snapshotUrl.search = ''; + snapshotUrl.hash = ''; + + return { repoUrl: snapshotUrl.toString(), ref: revRef ?? hashRef }; + } catch (error) { + if (error instanceof BadRequestException) { + throw error; + } + } + + return this.parseScpLikeGitSnapshot(snapshot); + } + + private parseScpLikeGitSnapshot(snapshot: string): { + repoUrl: string; + ref: string; + } { + const revMarker = '?rev='; + const revIndex = snapshot.indexOf(revMarker); + const hashIndex = snapshot.indexOf('#'); + + if (revIndex === -1 && hashIndex === -1) { + throw new BadRequestException('Source snapshot must pin a git ref'); + } + + if (revIndex !== -1) { + const repoUrl = snapshot.slice(0, revIndex); + const refWithPossibleHash = snapshot.slice(revIndex + revMarker.length); + const [revRef, hashRef] = refWithPossibleHash.split('#'); + + if (hashRef && revRef !== hashRef) { + throw new BadRequestException( + 'Source snapshot must not contain conflicting git refs', + ); + } + + return { repoUrl, ref: revRef }; + } + + return { + repoUrl: snapshot.slice(0, hashIndex), + ref: snapshot.slice(hashIndex + 1), + }; + } + private validateRepoUrl(repoUrl: string): string { + if (SCP_LIKE_GIT_URL_PATTERN.test(repoUrl)) { + return repoUrl; + } + let parsedUrl: URL; try { @@ -95,24 +147,23 @@ export class GithubService { throw new BadRequestException('Repository URL must be a valid URL'); } - if (parsedUrl.protocol !== 'https:') { - throw new BadRequestException('Repository URL must use HTTPS'); + if (!['https:', 'ssh:', 'git:'].includes(parsedUrl.protocol)) { + throw new BadRequestException( + 'Repository URL must use HTTPS, SSH, or git protocol', + ); } - if (parsedUrl.username || parsedUrl.password) { + if ( + parsedUrl.password || + (parsedUrl.protocol === 'https:' && parsedUrl.username) + ) { throw new BadRequestException('Repository URL must not contain secrets'); } - if (parsedUrl.hostname.toLowerCase() !== 'github.com') { - throw new BadRequestException('Only github.com repositories are allowed'); - } - const normalizedPath = parsedUrl.pathname.replace(/\/$/, ''); - if ( - !/^\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\.git)?$/.test(normalizedPath) - ) { + if (!/^\/[A-Za-z0-9._~/-]+(?:\.git)?$/.test(normalizedPath)) { throw new BadRequestException( - 'Repository URL must point to a GitHub repo', + 'Repository URL must point to a git repository', ); } @@ -122,4 +173,12 @@ export class GithubService { return parsedUrl.toString(); } + + private getRepoUrlPath(repoUrl: string): string { + if (SCP_LIKE_GIT_URL_PATTERN.test(repoUrl)) { + return repoUrl.slice(repoUrl.indexOf(':') + 1); + } + + return new URL(repoUrl).pathname; + } } From b05111e5dfd8bca076960a5a6f8dbd2d49fa2834 Mon Sep 17 00:00:00 2001 From: Andrey Gruzdev Date: Fri, 29 May 2026 19:37:10 +0200 Subject: [PATCH 5/5] fix(security): clean verifier hardening edge cases --- .../constants/validation.constants.spec.ts | 16 ++++++- .../controllers/verify/verify.controller.ts | 45 ++++++++++--------- .../services/github/github.service.spec.ts | 33 +++++++++++--- nest/src/services/github/github.service.ts | 23 ++++++---- 4 files changed, 80 insertions(+), 37 deletions(-) diff --git a/nest/src/constants/validation.constants.spec.ts b/nest/src/constants/validation.constants.spec.ts index 8b60772..57a05f8 100644 --- a/nest/src/constants/validation.constants.spec.ts +++ b/nest/src/constants/validation.constants.spec.ts @@ -1,4 +1,8 @@ -import { isValidCommitSha, isValidNearAccountId } from './validation.constants'; +import { + isValidCommitSha, + isValidGitRef, + isValidNearAccountId, +} from './validation.constants'; describe('validation constants', () => { it('should accept real NEAR account ID formats', () => { @@ -48,4 +52,14 @@ describe('validation constants', () => { expect(isValidCommitSha('a80bc29')).toBe(false); expect(isValidCommitSha('main')).toBe(false); }); + + it('should validate safe git refs', () => { + for (const ref of ['main', 'v1.2.3', 'feature/repro-build', 'a80bc29']) { + expect(isValidGitRef(ref)).toBe(true); + } + + for (const ref of ['--help', 'main;touch', 'foo..bar', 'foo@{bar}']) { + expect(isValidGitRef(ref)).toBe(false); + } + }); }); diff --git a/nest/src/controllers/verify/verify.controller.ts b/nest/src/controllers/verify/verify.controller.ts index 71c96d3..5774235 100644 --- a/nest/src/controllers/verify/verify.controller.ts +++ b/nest/src/controllers/verify/verify.controller.ts @@ -112,8 +112,8 @@ export class VerifyController { buildInfo = contractMetadata.build_info; - // Extract the repository URL and commit hash - const { repoUrl, sha } = this.githubService.parseSourceCodeSnapshot( + // Extract the repository URL and pinned git ref + const { repoUrl, ref } = this.githubService.parseSourceCodeSnapshot( buildInfo.source_code_snapshot, ); @@ -122,7 +122,7 @@ export class VerifyController { try { await this.githubService.clone(tempFolder, repoUrl); const repoPath = this.githubService.getRepoPath(tempFolder, repoUrl); - await this.githubService.checkout(repoPath, sha); + await this.githubService.checkout(repoPath, ref); } catch (error) { if ( error.message.includes('Authentication failed') || @@ -131,18 +131,15 @@ export class VerifyController { error.message.includes('remote: Repository not found') || error.message.includes('fatal: repository') ) { - await this.tempService.deleteFolder(tempFolder); return res.status(400).json({ message: `Repository is not publicly accessible: ${repoUrl}. Contract verification requires public repositories.`, }); } - await this.tempService.deleteFolder(tempFolder); throw error; // Re-throw other errors + } finally { + await this.cleanupTempFolder(tempFolder); } - // Clean up successful clone - await this.tempService.deleteFolder(tempFolder); - // Check Docker image availability if using Docker build environment if ( buildInfo.build_environment && @@ -227,8 +224,8 @@ export class VerifyController { // Contract metadata and buildInfo already fetched in pre-verification checks - // Extract the repository URL and the commit hash - const { repoUrl, sha } = this.githubService.parseSourceCodeSnapshot( + // Extract the repository URL and pinned git ref + const { repoUrl, ref } = this.githubService.parseSourceCodeSnapshot( buildInfo.source_code_snapshot, ); @@ -239,25 +236,17 @@ export class VerifyController { // Create a temporary folder to clone the repository for IPFS tempFolder = await this.tempService.createFolder(); - // Clone the repository and checkout the commit + // Clone the repository and checkout the pinned git ref await this.githubService.clone(tempFolder, repoUrl); const repoPath = this.githubService.getRepoPath(tempFolder, repoUrl); - await this.githubService.checkout(repoPath, sha); + await this.githubService.checkout(repoPath, ref); // Pin to IPFS this.logger.log(`Adding repository to IPFS for ${accountId}`); cid = await this.ipfsService.addFolder(repoPath); this.logger.log(`IPFS CID for ${accountId}: ${cid}`); } finally { - if (tempFolder) { - try { - await this.tempService.deleteFolder(tempFolder); - } catch (cleanupError) { - this.logger.error( - `Failed to clean up ${tempFolder}: ${cleanupError.message}`, - ); - } - } + await this.cleanupTempFolder(tempFolder); } // Store verification result @@ -282,4 +271,18 @@ export class VerifyController { return res.status(500).json({ message: error.message }); } } + + private async cleanupTempFolder(tempFolder: string | null): Promise { + if (!tempFolder) { + return; + } + + try { + await this.tempService.deleteFolder(tempFolder); + } catch (cleanupError) { + this.logger.error( + `Failed to clean up ${tempFolder}: ${cleanupError.message}`, + ); + } + } } diff --git a/nest/src/services/github/github.service.spec.ts b/nest/src/services/github/github.service.spec.ts index 04f4f93..d0bd477 100644 --- a/nest/src/services/github/github.service.spec.ts +++ b/nest/src/services/github/github.service.spec.ts @@ -55,7 +55,7 @@ describe('GithubService', () => { ); expect(result).toEqual({ repoUrl: 'https://github.com/near/cargo-near-new-project-template', - sha: ref, + ref, }); }); @@ -66,7 +66,7 @@ describe('GithubService', () => { ); expect(result).toEqual({ repoUrl: 'https://github.com/near/cargo-near-new-project-template.git', - sha: ref, + ref, }); }); @@ -77,7 +77,7 @@ describe('GithubService', () => { expect(result).toEqual({ repoUrl: 'https://gitlab.com/user/repo.git', - sha: 'v1.2.3', + ref: 'v1.2.3', }); }); @@ -88,7 +88,7 @@ describe('GithubService', () => { expect(result).toEqual({ repoUrl: 'ssh://git@gitlab.com/user/repo.git', - sha: 'feature/repro-build', + ref: 'feature/repro-build', }); }); @@ -99,7 +99,7 @@ describe('GithubService', () => { expect(result).toEqual({ repoUrl: 'git@gitlab.com:user/repo.git', - sha: 'a80bc29', + ref: 'a80bc29', }); }); @@ -122,7 +122,15 @@ describe('GithubService', () => { it('should reject local filesystem source snapshots', () => { expect(() => service.parseSourceCodeSnapshot('git+file:///tmp/repo?rev=main'), - ).toThrow('Repository URL must use HTTPS, SSH, or git protocol'); + ).toThrow('Repository URL must use HTTPS or SSH'); + }); + + it('should reject unauthenticated git protocol source snapshots', () => { + expect(() => + service.parseSourceCodeSnapshot( + 'git+git://github.com/user/repo?rev=main', + ), + ).toThrow('Repository URL must use HTTPS or SSH'); }); it('should get repo path', () => { @@ -138,8 +146,19 @@ describe('GithubService', () => { await expect(service.checkout('/tmp/repo', sha)).resolves.not.toThrow(); expect(execService.executeFile).toHaveBeenCalledWith( 'git', - ['-c', 'advice.detachedHead=false', 'checkout', '--detach', '--', sha], + ['-c', 'advice.detachedHead=false', 'checkout', '--detach', sha], { cwd: '/tmp/repo' }, ); }); + + it('should reject option-like refs before checkout', async () => { + await expect(service.checkout('/tmp/repo', '--help')).rejects.toThrow( + 'Git ref contains unsafe characters', + ); + expect(execService.executeFile).not.toHaveBeenCalledWith( + 'git', + expect.arrayContaining(['--help']), + expect.anything(), + ); + }); }); diff --git a/nest/src/services/github/github.service.ts b/nest/src/services/github/github.service.ts index a7b6279..b8a5101 100644 --- a/nest/src/services/github/github.service.ts +++ b/nest/src/services/github/github.service.ts @@ -28,9 +28,12 @@ export class GithubService { parseSourceCodeSnapshot(sourceCodeSnapshot: string): { repoUrl: string; - sha: string; + ref: string; } { - if (!sourceCodeSnapshot?.startsWith('git+')) { + if ( + typeof sourceCodeSnapshot !== 'string' || + !sourceCodeSnapshot.startsWith('git+') + ) { throw new BadRequestException('Source snapshot must use git+ URL format'); } @@ -43,7 +46,7 @@ export class GithubService { } const repoUrl = this.validateRepoUrl(rawRepoUrl); - return { repoUrl, sha: ref }; + return { repoUrl, ref }; } getRepoPath(tempFolder: string, repoUrl: string): string { @@ -62,7 +65,7 @@ export class GithubService { try { await this.execService.executeFile( 'git', - ['-c', 'advice.detachedHead=false', 'checkout', '--detach', '--', ref], + ['-c', 'advice.detachedHead=false', 'checkout', '--detach', ref], { cwd: repoPath }, ); this.logger.log(`Checkout completed successfully.`); @@ -147,10 +150,8 @@ export class GithubService { throw new BadRequestException('Repository URL must be a valid URL'); } - if (!['https:', 'ssh:', 'git:'].includes(parsedUrl.protocol)) { - throw new BadRequestException( - 'Repository URL must use HTTPS, SSH, or git protocol', - ); + if (!['https:', 'ssh:'].includes(parsedUrl.protocol)) { + throw new BadRequestException('Repository URL must use HTTPS or SSH'); } if ( @@ -166,6 +167,12 @@ export class GithubService { 'Repository URL must point to a git repository', ); } + const repoName = path.posix.basename(normalizedPath).replace(/\.git$/, ''); + if (!repoName || repoName === '.' || repoName === '..') { + throw new BadRequestException( + 'Repository URL must point to a git repository', + ); + } parsedUrl.pathname = normalizedPath; parsedUrl.search = '';