diff --git a/.github/workflows/npm_release_cli.yml b/.github/workflows/npm_release_cli.yml index 70dae07342..76a41e02e4 100644 --- a/.github/workflows/npm_release_cli.yml +++ b/.github/workflows/npm_release_cli.yml @@ -10,6 +10,8 @@ on: - 'packages/**' workflow_dispatch: +permissions: read-all + env: NPM_TAG: 'next' @@ -151,12 +153,23 @@ jobs: name: npm-package path: dist + - name: Generate provenance statement + run: | + TGZ_PATH=$(ls dist/nativescript-*.tgz | head -n1) + TGZ_NAME=$(basename "$TGZ_PATH") + TGZ_SHA=$(sha256sum "$TGZ_PATH" | awk '{ print $1 }') + PROV_PATH="dist/${TGZ_NAME%.tgz}.intoto.jsonl" + + cat > "$PROV_PATH" < body.md - uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 with: - artifacts: "dist/nativescript-*.tgz" + artifacts: "dist/nativescript-*.tgz,dist/nativescript-*.intoto.jsonl" bodyFile: "body.md" prerelease: ${{needs.build.outputs.npm_tag != 'latest'}} allowUpdates: true diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 7595c41d05..ebf35691f7 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -46,7 +46,7 @@ jobs: # - you want to enable the Branch-Protection check on a *public* repository, or # - you are installing Scorecards on a *private* repository # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. - # repo_token: ${{ secrets.SCORECARD_TOKEN }} + repo_token: ${{ secrets.SCORECARD_TOKEN }} # Public repositories: # - Publish results to OpenSSF REST API for easy access by consumers diff --git a/README.md b/README.md index 55da5866a1..7324ffcff2 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,12 @@ Get it using: `npm install -g nativescript` - [Extending the CLI](#extending-the-cli) - [Troubleshooting](#troubleshooting) - [How to Contribute](#how-to-contribute) +- [Scorecard Maintenance](#scorecard-maintenance) + - [1) Branch-Protection check (`?`) in Scorecard workflow](#1-branch-protection-check--in-scorecard-workflow) + - [2) Required branch/ruleset settings for higher Branch-Protection and Code-Review](#2-required-branchruleset-settings-for-higher-branch-protection-and-code-review) + - [3) Keep Token-Permissions high](#3-keep-token-permissions-high) + - [4) Signed-Releases check](#4-signed-releases-check) + - [5) Vulnerabilities check](#5-vulnerabilities-check) - [How to Build](#how-to-build) - [Get Help](#get-help) - [License](#license) @@ -344,6 +350,49 @@ To learn how to contribute to the code base, click [here](https://github.com/Nat [Back to Top][1] +Scorecard Maintenance +=== + +This repository tracks OpenSSF Scorecard. Use this checklist when score drops or checks become inconclusive. + +### 1) Branch-Protection check (`?`) in Scorecard workflow + +- Ensure `.github/workflows/scorecard.yml` uses `repo_token: ${{ secrets.SCORECARD_TOKEN }}`. +- Set `SCORECARD_TOKEN` as a repository Actions secret. +- If using a fine-grained PAT, set expiration to **366 days or less** (NativeScript org policy). +- If Branch-Protection still reports token incompatibility, use a PAT type compatible with Scorecard's Branch-Protection query path. + +### 2) Required branch/ruleset settings for higher Branch-Protection and Code-Review + +Apply to `main` and release branches: + +- Prevent force push and prevent branch deletion. +- Require pull request before merge. +- Require status checks to pass before merge. +- Require at least 2 approvals. +- Require code owner review. +- Dismiss stale approvals when new commits are pushed. +- Include administrators. + +### 3) Keep Token-Permissions high + +- Set top-level workflow permissions to read-only (for example `permissions: read-all`). +- Grant write permissions only at job level and only when needed (for example publish/release jobs). +- Keep GitHub Actions pinned to full commit SHAs. + +### 4) Signed-Releases check + +- Publish release assets with provenance/signature files. +- Keep release workflow attaching `*.intoto.jsonl` artifacts alongside release bundles. + +### 5) Vulnerabilities check + +- Keep runtime dependency vulnerabilities near zero. +- Run `npm audit --omit=dev` before release PRs. +- Update vulnerable dependencies quickly; for non-applicable findings, document and track mitigation clearly. + +[Back to Top][1] + How to Build === ``` diff --git a/lib/common/test/unit-tests/yok.ts b/lib/common/test/unit-tests/yok.ts index 5c63a18d08..32370fcc7b 100644 --- a/lib/common/test/unit-tests/yok.ts +++ b/lib/common/test/unit-tests/yok.ts @@ -5,6 +5,7 @@ import * as fs from "fs"; import { mkdtempSync } from "fs"; import { tmpdir } from "os"; import * as _ from "lodash"; +import * as fc from "fast-check"; import { ICliGlobal } from "../../definitions/cli-global"; import { ICommandParameter } from "../../definitions/commands"; import { IInjector } from "../../definitions/yok"; @@ -1096,6 +1097,85 @@ $injector.register("a", A); }); }); }); + + it("fuzzes one-level hierarchical command parsing with mixed casing", () => { + fc.assert( + fc.property( + fc + .stringMatching(/^[a-z][a-z0-9-]{0,15}$/) + .filter((value) => value.indexOf("|") === -1), + fc.array(fc.string(), { maxLength: 6 }), + fc.array(fc.boolean(), { minLength: 1, maxLength: 16 }), + ( + rawSubCommand: string, + remainingArguments: string[], + mixedCaseFlags: boolean[], + ) => { + setGlobalInjector(new Yok()); + const subCommand = rawSubCommand.slice(0, 16); + const commandName = `sample|${subCommand}`; + injector.requireCommand(commandName, "sampleFileName"); + + const commandTokenCharacters = subCommand.split(""); + const mixedCaseToken = commandTokenCharacters + .map((character, index) => { + const useUpperCase = + mixedCaseFlags[index % mixedCaseFlags.length]; + return useUpperCase + ? character.toUpperCase() + : character.toLowerCase(); + }) + .join(""); + + const result = injector.buildHierarchicalCommand("sample", [ + mixedCaseToken, + ...remainingArguments, + ]); + + assert.isDefined(result); + assert.deepStrictEqual(result.commandName, commandName); + assert.deepStrictEqual( + result.remainingArguments, + remainingArguments, + ); + }, + ), + { numRuns: 120 }, + ); + }); + + it("fuzzes two-level hierarchical command parsing and trailing args", () => { + fc.assert( + fc.property( + fc + .tuple( + fc.stringMatching(/^[a-z][a-z0-9]{0,10}$/), + fc.stringMatching(/^[a-z][a-z0-9]{0,10}$/), + ) + .filter(([firstToken, secondToken]) => firstToken !== secondToken), + fc.array(fc.string(), { maxLength: 5 }), + ([firstToken, secondToken], trailingArguments: string[]) => { + setGlobalInjector(new Yok()); + const commandName = `sample|${firstToken}|${secondToken}`; + injector.requireCommand(commandName, "sampleFileName"); + + const result = injector.buildHierarchicalCommand("sample", [ + firstToken, + secondToken, + ...trailingArguments, + ]); + + assert.isDefined(result); + assert.deepStrictEqual(result.commandName, commandName); + assert.deepStrictEqual( + result.remainingArguments, + trailingArguments, + ); + }, + ), + { numRuns: 120 }, + ); + }); }); it("adds whole class to public api when requirePublicClass is used", () => { diff --git a/package-lock.json b/package-lock.json index b223ef22a4..8b1f3b016f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "nativescript", - "version": "9.0.1", + "version": "9.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nativescript", - "version": "9.0.1", + "version": "9.0.3", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -17,7 +17,7 @@ "@nstudio/trapezedev-project": "7.2.3", "@rigor789/resolve-package-path": "1.0.7", "archiver": "7.0.1", - "axios": "1.13.2", + "axios": "1.13.5", "byline": "5.0.0", "chokidar": "4.0.3", "cli-table3": "0.6.5", @@ -56,7 +56,7 @@ "simple-git": "3.30.0", "simple-plist": "1.4.0", "source-map": "0.7.6", - "tar": "7.5.2", + "tar": "7.5.9", "ts-morph": "25.0.1", "tunnel": "0.0.6", "typescript": "5.7.3", @@ -105,6 +105,7 @@ "chai": "5.3.3", "chai-as-promised": "8.0.2", "conventional-changelog-cli": "^5.0.0", + "fast-check": "3.23.2", "grunt": "1.6.1", "grunt-contrib-clean": "2.0.1", "grunt-contrib-copy": "1.0.0", @@ -1169,22 +1170,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@npmcli/installed-package-contents": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz", - "integrity": "sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==", - "license": "ISC", - "dependencies": { - "npm-bundled": "^4.0.0", - "npm-normalize-package-bin": "^4.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/@npmcli/map-workspaces": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-5.0.1.tgz", @@ -1787,7 +1772,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2668,13 +2652,13 @@ } }, "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, @@ -3096,7 +3080,6 @@ "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -3883,7 +3866,6 @@ "integrity": "sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "meow": "^13.0.0" }, @@ -4792,6 +4774,29 @@ "node": ">=0.10.0" } }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -5512,7 +5517,6 @@ "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "dateformat": "~4.6.2", "eventemitter2": "~0.4.13", @@ -8140,7 +8144,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -8966,18 +8969,6 @@ "node": ">=0.10.0" } }, - "node_modules/npm-bundled": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", - "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", - "license": "ISC", - "dependencies": { - "npm-normalize-package-bin": "^4.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/npm-install-checks": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-8.0.0.tgz", @@ -8990,15 +8981,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/npm-normalize-package-bin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", - "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/npm-package-arg": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", @@ -10165,6 +10147,23 @@ "once": "^1.3.1" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/qr-image": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/qr-image/-/qr-image-3.2.0.tgz", @@ -11727,27 +11726,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/ssri": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", - "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/ssri/node_modules/minipass": { - "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/static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -12081,9 +12059,9 @@ } }, "node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.9.tgz", + "integrity": "sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -12241,7 +12219,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -12479,7 +12456,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index 5881ad3369..66cd3a82de 100644 --- a/package.json +++ b/package.json @@ -58,10 +58,10 @@ "@nativescript/doctor": "2.0.17", "@nativescript/hook": "3.0.4", "@npmcli/arborist": "9.1.8", - "@rigor789/resolve-package-path": "1.0.7", "@nstudio/trapezedev-project": "7.2.3", + "@rigor789/resolve-package-path": "1.0.7", "archiver": "7.0.1", - "axios": "1.13.2", + "axios": "1.13.5", "byline": "5.0.0", "chokidar": "4.0.3", "cli-table3": "0.6.5", @@ -100,7 +100,7 @@ "simple-git": "3.30.0", "simple-plist": "1.4.0", "source-map": "0.7.6", - "tar": "7.5.2", + "tar": "7.5.9", "ts-morph": "25.0.1", "tunnel": "0.0.6", "typescript": "5.7.3", @@ -143,6 +143,7 @@ "chai": "5.3.3", "chai-as-promised": "8.0.2", "conventional-changelog-cli": "^5.0.0", + "fast-check": "3.23.2", "grunt": "1.6.1", "grunt-contrib-clean": "2.0.1", "grunt-contrib-copy": "1.0.0",