From b7609dc996208087b340f4081d7c75937a340189 Mon Sep 17 00:00:00 2001 From: tanglearncode Date: Sun, 23 Aug 2026 11:58:15 +0800 Subject: [PATCH 1/2] ci: run the HOL AI plugin scanner on push and pull requests The awesome-ai-plugins catalog will not list a repository that does not run this scan itself: its contribution validator reads the listed repository's .github/workflows, requires a push or pull_request workflow that uses hashgraph-online/ai-plugin-scanner-action, and only then re-scans the repository against the same 80/142 score and high-severity gate this job enforces. The job is scoped to what a scan actually needs: contents: read, no secrets, no persisted checkout token, and both actions pinned to an immutable commit so a moved tag cannot change what runs. --- .github/workflows/plugin-scanner.yml | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/plugin-scanner.yml diff --git a/.github/workflows/plugin-scanner.yml b/.github/workflows/plugin-scanner.yml new file mode 100644 index 0000000..3eaa31c --- /dev/null +++ b/.github/workflows/plugin-scanner.yml @@ -0,0 +1,40 @@ +name: Plugin scanner + +# The awesome-ai-plugins catalog gates every listing on this scan: its +# validator requires the listed repository to run the HOL AI Plugin Scanner +# from a push or pull_request workflow, then re-scans this repository against +# the same thresholds it enforces here. +# https://github.com/hashgraph-online/awesome-ai-plugins/blob/main/SCANNER_GUIDE.md +on: + pull_request: + push: + branches: [main] + +# The scan only reads the checked-out tree. It needs no secrets and no write +# scope, so it gets neither. +permissions: + contents: read + +concurrency: + group: plugin-scanner-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + scan: + name: HOL AI plugin scanner + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + # Both actions are pinned to an immutable commit rather than a tag: a + # moved tag would otherwise silently change the code this job runs. + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + # Nothing here pushes, so the job never needs the checkout token. + persist-credentials: false + - uses: hashgraph-online/ai-plugin-scanner-action@b11c05ef7ee17c7d84299de8a245e364c1cb070c # v1.2.533 + with: + plugin_dir: "." + # The catalog's own gate: score at least 80/142, and fail the job on + # any high or critical finding. + min_score: 80 + fail_on_severity: high From 9923c06f30a88760411055c6575e290238aeae9f Mon Sep 17 00:00:00 2001 From: tanglearncode Date: Sun, 23 Aug 2026 12:05:59 +0800 Subject: [PATCH 2/2] ci: keep the scanner gate on shipped code, not on the tests The scan the gate runs reports seven high findings, all of them in test files and all of them false: tests/conversation-evidence.test.mjs and tests/extensions.test.mjs carry deliberately realistic fake credentials because the assertion is that NeatContext keeps material like that out of a saved context, and five host tests spawn bridges with spawn(process.execPath, [script, ...args]), which is the structured-argument form the shell-injection rule's own remediation asks for. .plugin-scanner.toml suppresses those two rules under the test directories and nowhere else, so a real finding in shared/core/ or in a host's src/ still fails the build. The scanner discovers the file from the repository root on its own, which is what makes it apply to the catalog's re-scan of a fresh clone as well as to our own workflow. Scanning a clean clone with the gate's own thresholds now exits 0 at 85/100 with no high or critical findings. --- .plugin-scanner.toml | 30 ++++++++++++++++++++++++++++++ CONTRIBUTING.md | 29 +++++++++++++++++++++++++++++ README.md | 1 + 3 files changed, 60 insertions(+) create mode 100644 .plugin-scanner.toml diff --git a/.plugin-scanner.toml b/.plugin-scanner.toml new file mode 100644 index 0000000..96e388f --- /dev/null +++ b/.plugin-scanner.toml @@ -0,0 +1,30 @@ +# HOL AI Plugin Scanner configuration. +# +# The scanner picks this file up automatically from the repository root, so it +# applies both to .github/workflows/plugin-scanner.yml and to the re-scan the +# awesome-ai-plugins catalog runs against a fresh clone of this repository. +# +# HARDCODED_SECRET and SHELL_INJECTION_PATTERN stay fully enabled on every line +# of shipped host code. They are suppressed only under the test directories, +# where both rules fire on the tests themselves: +# +# * tests/conversation-evidence.test.mjs and tests/extensions.test.mjs hold +# deliberately realistic fake credentials ("API_KEY=sk-...", "password=", +# "PD_TOKEN") precisely because the assertion is that NeatContext keeps +# material like that out of a saved context. Defusing the fixture would +# defuse the test. +# * The host tests start bridges with spawn(process.execPath, [script, +# ...args]) — the structured-argument form the rule's own remediation asks +# for. No shell is involved. +# +# Patterns are fnmatch rather than glob, so `*` crosses path separators. The +# backslash variants are here because the scanner reports native paths: without +# them a maintainer running the scan on Windows would not see what CI sees. + +[scanner] +ignore_paths = [ + "tests/*", + "*/tests/*", + 'tests\*', + '*\tests\*', +] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6a69418..8660162 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,6 +76,35 @@ Please keep the plugin runtime self-contained and host-neutral. Shared local storage behavior belongs in `shared/core/`; host wording and adapters stay in their host packages. +## Plugin scanner + +`.github/workflows/plugin-scanner.yml` runs the [HOL AI Plugin +Scanner](https://github.com/hashgraph-online/hol-guard) on every pull request +and every push to `main`, and fails the build on a score below 80 or on any +high or critical finding. The gate is not only ours: the +[awesome-ai-plugins](https://github.com/hashgraph-online/awesome-ai-plugins) +catalog lists a repository only if that repository runs this scan itself, and +then re-scans a fresh clone of it against the same two thresholds. + +Run what CI runs: + +```bash +pipx run plugin-scanner scan . --min-score 80 --fail-on-severity high +``` + +`.plugin-scanner.toml` in the repository root is discovered automatically — by +your local run and by the catalog's re-scan alike. It suppresses +`HARDCODED_SECRET` and `SHELL_INJECTION_PATTERN` under the test directories and +nowhere else. The credentials there are fake on purpose, because the assertion +is that NeatContext keeps material like that out of a saved context, and the +host tests start bridges with `spawn(process.execPath, [script, ...args])` — +the structured-argument form the rule's own remediation asks for. + +Both rules stay live on every line of shipped host code, so a real finding in +`shared/core/` or in a host's `src/` still fails the build. Widen the ignore +list only when a finding is genuinely about the test rather than about the +code, and say which in the pull request. + ## Releases There are two release streams, because the hosts are distributed differently. diff --git a/README.md b/README.md index 7661780..f2f89bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # NeatContext plugins [![CI](https://github.com/XTSoftwareLabs/neatcontext-plugins/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/XTSoftwareLabs/neatcontext-plugins/actions/workflows/ci.yml) +[![Plugin scanner](https://github.com/XTSoftwareLabs/neatcontext-plugins/actions/workflows/plugin-scanner.yml/badge.svg?branch=main)](https://github.com/XTSoftwareLabs/neatcontext-plugins/actions/workflows/plugin-scanner.yml) [![Version](https://img.shields.io/github/package-json/v/XTSoftwareLabs/neatcontext-plugins)](package.json) [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933?logo=node.js&logoColor=white)](package.json) [![License](https://img.shields.io/github/license/XTSoftwareLabs/neatcontext-plugins)](LICENSE)