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 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)