diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 67814cdd..241f3a69 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -5,6 +5,7 @@ First off, thanks for taking the time to contribute! ❤️ All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: +> > - Star the project > - Tweet about it > - Refer this project in your project's readme @@ -64,7 +65,7 @@ We use GitHub issues to track bugs and errors. If you run into an issue with the - Open an [Issue](https://github.com/AikidoSec/firewall-node/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) - Explain the behavior you would expect and the actual behavior. -- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. +- Please provide as much context as possible and describe the _reproduction steps_ that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. - Provide the information you collected in the previous section. Once it's filed: @@ -96,12 +97,12 @@ Enhancement suggestions are tracked as [GitHub issues](https://github.com/Aikido ### Your First Code Contribution - clone the repository to your local machine -- run `$ make install` to install dependencies -- run `$ make build` to build the library -- run `$ make watch` to watch for changes and rebuild the library -- run `$ make test` to run tests using tap -- run `$ make end2end` to run end-to-end tests using tap -- run `$ make lint` to run ESLint +- run `$ npm install` to install dependencies +- run `$ npm run build` to build the library +- run `$ npm run watch` to watch for changes and rebuild the library +- run `$ npm t` to run tests using tap +- run `$ npm run end2end` to run end-to-end tests using tap +- run `$ npm run lint` to run ESLint ## Styleguides diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6f516012..5a004787 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -39,8 +39,8 @@ jobs: run: | sudo apt-get update sudo apt-get install -y wrk - - run: make install - - run: make build + - run: npm install + - run: npm run build - name: Run NoSQL Injection Benchmark run: cd benchmarks/nosql-injection && AIKIDO_CI=true node benchmark.js - name: Run SQL Injection Benchmark diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 134f7443..def909fa 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -27,16 +27,16 @@ jobs: registry-url: "https://registry.npmjs.org" scope: "@aikidosec" - name: Install dependencies - run: make install + run: npm install - name: Get the version id: get_version run: echo "tag=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT - name: Set the version run: cd library && npm --no-git-tag-version version ${{ steps.get_version.outputs.tag }} - name: Build the library - run: make build + run: npm run build - name: Linting - run: make lint + run: npm run lint - name: Publish to NPM run: | if [ "${{ github.event.release.prerelease }}" = "true" ]; then diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index 46dd0af2..649a2187 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -63,6 +63,6 @@ jobs: - name: Build and run server run: | cd end2end/server && docker build -t server . && docker run -d -p 5874:3000 server - - run: make install - - run: make build - - run: make end2end + - run: npm install + - run: npm run build + - run: npm run end2end diff --git a/.github/workflows/lint-code.yml b/.github/workflows/lint-code.yml index c17f2ccb..1c93bec2 100644 --- a/.github/workflows/lint-code.yml +++ b/.github/workflows/lint-code.yml @@ -14,6 +14,6 @@ jobs: node-version: ${{ matrix.node-version }} cache: "npm" cache-dependency-path: "**/package-lock.json" - - run: make install-lib-only - - run: make build - - run: make lint + - run: npm run install-lib-only + - run: npm run build + - run: npm run lint diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index 541f89bf..74f695f8 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -76,9 +76,9 @@ jobs: - name: Add local.aikido.io to /etc/hosts run: | sudo echo "127.0.0.1 local.aikido.io" | sudo tee -a /etc/hosts - - run: make install-lib-only - - run: make build - - run: make test-ci + - run: npm run install-lib-only + - run: npm run build + - run: npm run test:ci - name: "Upload coverage" uses: codecov/codecov-action@v4.0.1 with: diff --git a/Makefile b/Makefile index 1f9a2d37..3308b57d 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,3 @@ -INTERNALS_VERSION = v0.1.34 -INTERNALS_URL = https://github.com/AikidoSec/zen-internals/releases/download/$(INTERNALS_VERSION) -TARBALL = zen_internals.tgz -CHECKSUM_FILE = zen_internals.tgz.sha256sum -INTERNALS_DIR = library/internals - -.PHONY: containers -containers: - cd sample-apps && docker-compose up -d --remove-orphans - .PHONY: express-mongodb express-mongodb: cd sample-apps/express-mongodb && AIKIDO_DEBUG=true AIKIDO_BLOCKING=true node app.js @@ -87,67 +77,3 @@ fastify-clickhouse: .PHONY: hono-prisma hono-prisma: cd sample-apps/hono-prisma && AIKIDO_DEBUG=true AIKIDO_BLOCK=true node app.js - -NPM_INSTALL_CMD := $(if $(CI),ci,install) - -.PHONY: install-lib-only -install-lib-only: - mkdir -p build - node scripts/copyPackageJSON.js - touch build/index.js - npm $(NPM_INSTALL_CMD) - cd library && npm $(NPM_INSTALL_CMD) - -.PHONY: install -install: install-lib-only - cd end2end && npm $(NPM_INSTALL_CMD) - node scripts/install.js - -.PHONY: build -build: $(INTERNALS_DIR)/zen_internals.js - mkdir -p build - rm -r build - cd library && npm run build - cp README.md build/README.md - cp LICENSE build/LICENSE - cp library/package.json build/package.json - mkdir -p build/internals - cp $(INTERNALS_DIR)/zen_internals_bg.wasm build/internals/zen_internals_bg.wasm - -$(INTERNALS_DIR)/zen_internals.js: Makefile - curl -L $(INTERNALS_URL)/$(TARBALL) -o $(INTERNALS_DIR)/$(TARBALL) - curl -L $(INTERNALS_URL)/$(CHECKSUM_FILE) -o $(INTERNALS_DIR)/$(CHECKSUM_FILE) - cd $(INTERNALS_DIR) && sha256sum -c $(CHECKSUM_FILE) - tar -xzf $(INTERNALS_DIR)/$(TARBALL) -C $(INTERNALS_DIR) - touch $@ - rm $(INTERNALS_DIR)/zen_internals.d.ts - rm $(INTERNALS_DIR)/$(TARBALL) - rm $(INTERNALS_DIR)/$(CHECKSUM_FILE) - -.PHONY: watch -watch: build - cd library && npm run build:watch - -.PHONY: test -test: - cd library && npm run test - -.PHONY: test-ci -test-ci: - cd library && npm run test:ci - -.PHONY: lint -lint: - cd library && npm run lint - -.PHONY: end2end -end2end: - cd end2end && npm run test - -benchmark: build - cd benchmarks/nosql-injection && AIKIDO_CI=true node benchmark.js - cd benchmarks/shell-injection && node benchmark.js - cd benchmarks/sql-injection && node benchmark.js - cd benchmarks/hono-pg && node benchmark.js - cd benchmarks/api-discovery && node benchmark.js - cd benchmarks/express && node benchmark.js diff --git a/package-lock.json b/package-lock.json index 329b9f92..eadf12af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,12 +5,328 @@ "packages": { "": { "name": "firewall-node", + "hasInstallScript": true, "devDependencies": { - "prettier": "^3.2.4" + "prettier": "^3.2.4", + "tar": "^7.4.3" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "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.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/prettier": { - "version": "3.2.5", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, "license": "MIT", "bin": { @@ -22,6 +338,304 @@ "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } } } } diff --git a/package.json b/package.json index 2604a985..ff370e26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,20 @@ { "name": "firewall-node", "devDependencies": { - "prettier": "^3.2.4" + "prettier": "^3.2.4", + "tar": "^7.4.3" + }, + "scripts": { + "install": "node scripts/install.js", + "install-lib-only": "node scripts/install.js --lib-only", + "containers": "cd sample-apps && docker compose up -d --remove-orphans", + "build": "node scripts/build.js", + "watch": "cd library && npm run build:watch", + "test": "cd library && npm run test", + "test:ci": "cd library && npm run test:ci", + "lint": "cd library && npm run lint", + "end2end": "cd end2end && npm run test", + "format": "prettier --write .", + "benchmark": "node scripts/benchmark.js" } } diff --git a/scripts/benchmark.js b/scripts/benchmark.js new file mode 100644 index 00000000..bcc15719 --- /dev/null +++ b/scripts/benchmark.js @@ -0,0 +1,31 @@ +const { exec } = require("child_process"); +const { promisify } = require("util"); +const execAsync = promisify(exec); +const { scanForSubDirsWithPackageJson } = require("./helpers/fs"); +const { join } = require("path"); + +async function main() { + const benchmarks = await scanForSubDirsWithPackageJson("benchmarks"); + + for (const benchmark of benchmarks) { + console.log(`Running ${benchmark}`); + const output = await execAsync("node benchmark.js", { + cwd: join(__dirname, "..", benchmark), + env: { + ...process.env, + AIKIDO_CI: "true", + }, + }); + console.log(output.stdout); + console.error(output.stderr); + console.log(`Finished running ${benchmark}`); + } +} + +(async () => { + try { + await main(); + } catch (error) { + console.error(error); + } +})(); diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 00000000..7b589e75 --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,82 @@ +const { rm, copyFile, mkdir, readFile } = require("fs/promises"); +const { join } = require("path"); +const { exec } = require("child_process"); +const { promisify } = require("util"); +const { fileExists } = require("./helpers/fs"); +const { downloadFile, verifyFileHash } = require("./helpers/internals"); +const execAsync = promisify(exec); + +// Zen Internals configuration +const INTERNALS_VERSION = "v0.1.34"; +const INTERNALS_URL = `https://github.com/AikidoSec/zen-internals/releases/download/${INTERNALS_VERSION}`; +// --- + +const rootDir = join(__dirname, ".."); +const buildDir = join(rootDir, "build"); +const libDir = join(rootDir, "library"); + +async function main() { + // Delete build directory if it exists + if (await fileExists(buildDir)) { + await rm(buildDir, { recursive: true }); + } + + await execAsync(`npm run build`, { + cwd: libDir, + }); + + // Copy additional files to build directory + await copyFile( + join(rootDir, "library", "package.json"), + join(buildDir, "package.json") + ); + await copyFile(join(rootDir, "README.md"), join(buildDir, "README.md")); + await copyFile(join(rootDir, "LICENSE"), join(buildDir, "LICENSE")); + + await dlZenInternals(); + + console.log("Build successful"); + process.exit(0); +} + +// Download Zen Internals tarball and verify checksum +async function dlZenInternals() { + const internalsDir = join(libDir, "internals"); + + const tarballFile = "zen_internals.tgz"; + const checksumFile = "zen_internals.tgz.sha256sum"; + + await mkdir(internalsDir, { recursive: true }); + + // Check if the wanted version of Zen Internals is already installed + const versionCacheFile = join(internalsDir, ".installed_version"); + const installedVersion = (await fileExists(versionCacheFile)) + ? await readFile(versionCacheFile, "utf8") + : null; + if (installedVersion === INTERNALS_VERSION) { + console.log("Zen Internals already installed. Skipping download."); + return; + } + + await downloadFile( + `${INTERNALS_URL}/${tarballFile}`, + join(internalsDir, tarballFile) + ); + await downloadFile( + `${INTERNALS_URL}/${checksumFile}`, + join(internalsDir, checksumFile) + ); + await verifyFileHash(join(internalsDir, tarballFile)); + + await rm(join(internalsDir, tarballFile)); + await rm(join(internalsDir, checksumFile)); +} + +(async () => { + try { + await main(); + } catch (error) { + console.error(error); + process.exit(1); + } +})(); diff --git a/scripts/helpers/fs.js b/scripts/helpers/fs.js new file mode 100644 index 00000000..310f3b86 --- /dev/null +++ b/scripts/helpers/fs.js @@ -0,0 +1,37 @@ +const { readdir, access, constants } = require("fs/promises"); +const { join } = require("path"); + +async function fileExists(path) { + try { + await access(path, constants.F_OK); + return true; + } catch { + return false; + } +} + +/** + * Check for subdirectories with a package.json file in the given directory + */ +async function scanForSubDirsWithPackageJson(dir) { + const dirPath = join(__dirname, "../..", dir); + const files = await readdir(dirPath, { withFileTypes: true }); + + const results = []; + + for (const file of files) { + if (file.isDirectory()) { + const packageJsonPath = join(dirPath, file.name, "package.json"); + if (await fileExists(packageJsonPath)) { + results.push(join(dir, file.name)); + } + } + } + + return results; +} + +module.exports = { + fileExists, + scanForSubDirsWithPackageJson, +}; diff --git a/scripts/helpers/internals.js b/scripts/helpers/internals.js new file mode 100644 index 00000000..f3a1c074 --- /dev/null +++ b/scripts/helpers/internals.js @@ -0,0 +1,42 @@ +const { createWriteStream, createReadStream } = require("fs"); +const { Readable } = require("stream"); +const { finished, pipeline } = require("stream/promises"); +const { extract } = require("tar"); +const { readFile } = require("fs/promises"); +const { createHash } = require("crypto"); + +async function downloadFile(url, path) { + const stream = createWriteStream(path); + const { ok, body } = await fetch(url); + if (!ok) { + throw new Error(`Failed to download file from ${url}`); + } + await finished(Readable.fromWeb(body).pipe(stream)); +} + +async function extractTar(path, dest) { + await extract({ file: path, sync: false, cwd: dest }); +} + +async function verifyFileHash(filepath) { + const expectedHash = (await readFile(`${filepath}.sha256sum`, "utf8")).split( + " " + )[0]; + const input = createReadStream(filepath); + const hashBuilder = createHash("sha256"); + await pipeline(input, hashBuilder); + + const hash = hashBuilder.digest("hex"); + + if (hash !== expectedHash) { + console.log(`Expected: ${expectedHash}`); + console.log(`Actual: ${hash}`); + throw new Error(`File hash mismatch for ${filepath}`); + } +} + +module.exports = { + downloadFile, + extractTar, + verifyFileHash, +}; diff --git a/scripts/install.js b/scripts/install.js index 1d87c6de..75730496 100644 --- a/scripts/install.js +++ b/scripts/install.js @@ -1,86 +1,87 @@ -const { readdir, stat, access, constants } = require("fs/promises"); +const { fileExists, scanForSubDirsWithPackageJson } = require("./helpers/fs"); const { join } = require("path"); const { exec } = require("child_process"); const { promisify } = require("util"); +const { writeFile } = require("fs/promises"); const execAsync = promisify(exec); -function getInstallCmd() { - if (process.env.CI) { - return "npm ci"; - } - return "npm install"; +const projectRoot = join(__dirname, ".."); + +// If script is called with arg --ci, set env CI to true +if (process.argv.includes("--ci")) { + process.env.CI = "true"; } async function main() { - const sampleAppsDir = join(__dirname, "../sample-apps"); - const sampleApps = await readdir(sampleAppsDir); - - await Promise.all( - sampleApps.map(async (file) => { - const stats = await stat(join(sampleAppsDir, file)); - - if ( - !stats.isFile() && - (await fileExists(join(sampleAppsDir, file, "package.json"))) - ) { - await installSampleAppDeps(file); - } - }) - ); - - const benchmarksDir = join(__dirname, "../benchmarks"); - const benchmarks = await readdir(benchmarksDir); - - await Promise.all( - benchmarks.map(async (file) => { - const stats = await stat(join(benchmarksDir, file)); - - if ( - !stats.isFile() && - (await fileExists(join(benchmarksDir, file, "package.json"))) - ) { - await installBenchmarkDeps(file); - } - }) - ); -} + await prepareBuildDir(); -async function installSampleAppDeps(sampleApp) { - console.log(`Installing dependencies for ${sampleApp}`); + const libOnly = process.argv.includes("--lib-only"); - try { - await execAsync(getInstallCmd(), { - cwd: join(__dirname, "../sample-apps", sampleApp), - }); - console.log(`Dependencies installed for ${sampleApp}`); - } catch (error) { - console.error(`Failed to install dependencies for ${sampleApp}`); - console.error(error); - process.exit(1); + const installDirs = libOnly ? ["library"] : ["library", "end2end"]; + const scanForSubDirs = libOnly ? [] : ["sample-apps", "benchmarks"]; + + for (const dir of scanForSubDirs) { + const subDirs = await scanForSubDirsWithPackageJson(dir); + installDirs.push(...subDirs); } + + await Promise.all(installDirs.map(installDependencies)); + + console.log("Successfully installed all dependencies"); + process.exit(0); } -async function installBenchmarkDeps(benchmark) { - console.log(`Installing dependencies for ${benchmark}`); +/** + * Install dependencies for a given folder + */ +async function installDependencies(folder) { + console.log(`Installing dependencies for ${folder}`); + + const cmd = process.env.CI ? "npm ci" : "npm install"; try { - await execAsync(getInstallCmd(), { - cwd: join(__dirname, "../benchmarks", benchmark), + await execAsync(cmd, { + cwd: join(projectRoot, folder), }); - console.log(`Dependencies installed for ${benchmark}`); + console.log(`Installed dependencies for ${folder}`); } catch (error) { - console.error(`Failed to install dependencies for ${benchmark}`); + console.error(`Failed to install dependencies for ${folder}`); console.error(error); process.exit(1); } } -async function fileExists(path) { +/** + * Prepare the build directory + */ +async function prepareBuildDir() { try { - await access(path, constants.F_OK); - return true; - } catch { - return false; + const pkg = require(join(__dirname, "../library/package.json")); + + // We're going to remove the devDependencies from the package.json + // Otherwise they will show up in every lock file + // whenever we add a new dev dependency to the library + delete pkg.devDependencies; + + // If the build folder doesn't exist, create it + const buildDirPath = join(__dirname, "../build"); + if (!(await fileExists(buildDirPath))) { + await mkdir(buildDirPath); + } + + await writeFile( + join(buildDirPath, "package.json"), + JSON.stringify(pkg, null, 2) + ); + + // Create empty index.js file if it doesn't exist + if (!(await fileExists(join(buildDirPath, "index.js")))) { + await writeFile(join(buildDirPath, "index.js"), ""); + } + } catch (error) { + console.error(`Failed to prepare build directory`); + console.error(error); + process.exit(1); } } diff --git a/scripts/run-tap.js b/scripts/run-tap.js index ed96e2f8..3d83deaa 100644 --- a/scripts/run-tap.js +++ b/scripts/run-tap.js @@ -6,6 +6,11 @@ const minor = parseInt(version[1], 10); let args = "--allow-incomplete-coverage"; +// If script is called with arg --ci, set env CI to true +if (process.argv.includes("--ci")) { + process.env.CI = "true"; +} + if (process.env.CI) { args += " --coverage-report=lcov"; }