
zanadir
io.github.MustacheCase由 GitHub 账号 MustacheCase 发布v0.31.0更新于 Nov 7, 2026
Find what a repository's CI is missing, and which security checks cannot fail the build.
概览
通过 MCP 提供 zanadir 的只读 CI/CD 安全检查:评估仓库流水线控制项,并给出可直接粘贴的修复配置。
- 可以做什么
- 扫描本地检出目录或公开 GitHub 仓库,返回 JSON 报告:覆盖评分、缺失类别及建议工具、被削弱控制项及其评级
- 针对缺口输出可直接粘贴的 CI 配置,不写入任何文件
- 列出仓库中发现的 agent skill 及其问题
- 通过排除指定类别来缩小扫描或修复范围
- 适用场景
- 适合让助手审查、加固或修改仓库的 CI 配置,或在新增工具前判断现有安全扫描器是否真的能让构建失败。
- 运行要求
- 作为本地进程运行(zanadir mcp),需已安装 zanadir 可执行文件或使用 Docker 镜像;MCP 注册表中的条目以 Docker 运行并只读挂载仓库。扫描范围限于服务器 --dir 指定的目录内。匿名 GitHub 抓取仅限公开仓库;访问私有仓库或提高速率限制需以 --repo-token 启动并设置 GITHUB_TOKEN 或 GH_TOKEN。
安装
在 SourceWeft 中
- 打开 控制台中的 zanadir,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
[zanadir coverage] [Go Reference] [Test coverage] [Latest release] [License: MIT]
zanadir is an open-source CI/CD security tool. It reads a repository's pipeline, from GitHub Actions to GitLab CI, and reports which security and quality checks are missing, or wired so they cannot fail a build.
What it answers
- What is my pipeline missing? Thirteen categories, from dependency scanning and secrets detection to SBOMs, workflow hardening and agent skill scanning, each checked against the tools that cover it, with suggestions that fit the repository's languages.
- Does my CI actually block on security findings? A scanner with
continue-on-error,|| true, a push-only trigger orallow_failure, or one that only reports unless told to fail, liketrivywithout--exit-code 1, is present but cannot stop a merge. zanadir grades each control enforcing, partial, advisory or unknown rather than just covered; see Effectiveness. - Is that check required to merge? With
--required-checks, a control only counts as enforcing when branch protection or a ruleset requires it. - What do I add?
zanadir fixprints ready-to-paste CI configuration for the gaps, or writes a workflow with--write.
It reads GitHub Actions, GitLab CI, CircleCI, Bitbucket Pipelines, Azure
Pipelines and Jenkins, from a checkout (--dir) or straight from GitHub
(--repo), and reports as a table, JSON, SARIF or Markdown. To see it on a
public repository first, scan it in your browser.
For how it maps to the OWASP Top 10 CI/CD Security Risks, and how it differs
from OpenSSF Scorecard, see the mapping
and the comparison.
How common are these gaps? The State of CI security 2026 scanned 1,000 of the most-starred GitHub repositories: 511 declare no dependency, secret or code scanning at all, and 1 in 6 of those that run a scanner in CI have one that cannot fail the build.
Installation
Try it on any public repository without installing anything, in your browser at mustachecase.github.io/scan (zanadir runs there as WebAssembly, and the repository's files never leave your browser), or with Docker:
To install it, download the archive for Linux, macOS or Windows (amd64 or
arm64) from the latest release.
checksums.txt lists each archive's SHA-256, and every archive has a build
provenance attestation:
Or with Homebrew (the tap is this repository) or Go:
In GitHub Actions, use zanadir-action. This workflow reports on every pull request without failing it:
When you are ready to gate, add enforce: true (fail on any uncovered
category), fail-on (only the categories you name) or fail-on-weakened. To
post the report as a pull request comment, see
Posting the report to a pull request.
zanadir and OpenSSF Scorecard
OpenSSF Scorecard and zanadir answer different questions, and work well together.
Use Scorecard for a project-wide security posture, and zanadir to find out whether the CI you have would actually stop a bad change, and what to add.
Using zanadir with AI coding agents
To have a coding agent check CI with zanadir before changing it, add this to
the repository's AGENTS.md, CLAUDE.md or equivalent:
As an MCP server
zanadir mcp serves three read-only tools over the Model Context Protocol, so
any agent that speaks MCP can grade CI, and check agent skills, before and
after changing them:
scanreturns the JSON report: the score, uncovered categories with suggested tools, and weakened controls with their verdicts;fixreturns ready-to-paste configuration for the gaps, and writes nothing;skillsreturnszanadir skills' JSON: the skills found and their problems.
Each takes dir (a local checkout, by default the current directory) or
repo (github.com/owner/name[@ref], read without cloning); scan and
fix also take excludedCategories.
An agent can be steered by what it reads, so the server keeps its tools to
what you started it with. A dir must be inside --dir (by default the
directory the server starts in); a relative one is read from there. repo
fetches are anonymous, so they reach public repositories only, unless you
start the server with --repo-token to let them use GITHUB_TOKEN or
GH_TOKEN.
It is listed in the MCP Registry
as io.github.MustacheCase/zanadir, where it runs from the Docker image with
your repository mounted read-only. For other clients, such as Cursor, VS Code
or Windsurf:
As a Claude Code plugin
In Claude Code, the zanadir plugin brings both: the MCP server, and a skill
that scans, reads your CI configuration, recommends fixes in priority order,
and edits files only after you choose. The server runs the installed
zanadir if it is 0.18.0 or later, and otherwise the Docker image.
Then ask Claude Code to review or harden your CI, or run /zanadir:improve-ci.
llms.txt summarises zanadir for agents that read the repository, and is
served at mustachecase.github.io/llms.txt
for those that look at a site's root.
Features
- 📂 Scan: Analyze the repository for CI/CD enhancement suggestions, including security services and best practices.
- 🔍 CI Analysis: Grades whether each control can actually fail a build, not only whether it is present.
- 🛠️ Fix: Prints, or writes as a workflow, CI configuration for the categories a scan reports as missing.
- 🚀 Open Source: Contributions are welcome to enhance Zanadir's capabilities!
Supported CI Actions
Zanadir currently supports:
- GitHub Actions
- CircleCI
- GitLab
- Bitbucket Pipelines
- Azure Pipelines
- Jenkins
Future work will include support for:
- Tekton
Categories We Suggest
Zanadir analyzes repositories in the following categories. The name in bold is
the exact value to pass to --excluded-categories (matching is
case-insensitive):
- 🛡️ SCA: Software Composition Analysis
- 🔐 Secrets Detection: Secrets Management
- 📜 License Compliance: License Compliance
- 🛠️ End Of Life: End-of-Life Software Packages
- 📊 Coverage: Test Coverage
- 📊 Performance Testing: Test Performance and Reliability
- 🧑💻 Linter: Code Linting
- 🧪 Unit Tests: Automated Unit Testing
- 🔎 SAST: Static Application Security Testing
- 🏗️ IaC Security: Infrastructure-as-Code Misconfiguration Scanning
- 🔗 Supply Chain: SBOM Generation, Signing and Provenance
- 🧱 Workflow Security: Hardening of the pipeline definition itself
- 🤖 Agent Skill Security: Scanning agent skills (
SKILL.md) for prompt injection and malicious code
Usage Examples
Basic Usage
Scan a repository for CI/CD improvement suggestions:
Or scan a GitHub repository without cloning it:
--repo lists the repository's files at one commit and downloads only the
ones zanadir reads (CI and build files, plus the start of JSON files for
infrastructure detection) into a temporary directory. Every other file is an
empty placeholder, since only its name matters. It then scans the directory
exactly as a checkout and removes it. That is three GitHub API requests per
scan, however large the repository. A repository too large to list in one
request is downloaded as an archive instead. Public repositories need no
token; set GITHUB_TOKEN (or GH_TOKEN) to reach private ones or raise
GitHub's rate limit of 60 requests an hour without one. GitLab and Bitbucket
repositories still need a clone.
Output Formats
Zanadir supports three output formats: table (default), JSON and SARIF.
Table Output (Default)
Sample Output:
The covered categories come last, each with the tools found for it. A tool
whose run cannot block a merge shows its grade (see
Effectiveness). A rule that covers several
tools, such as the test command one, names the one it matched: pytest,
go test or mvn.
When nothing is missing, the scan says so rather than printing an empty table, and lists what covers each category:
The headline is bold on an interactive terminal. Colour is omitted when the
output is piped or written with --output-file, and when NO_COLOR is set.
JSON Output
One object with the score, the CI systems whose configuration was found, the
uncovered categories and the tools that cover them, the covered categories whose tooling cannot fully protect the
repository, the categories left out of the score, and every covered category
with the tools found for it. Every list is an array, empty rather than null
when there is nothing in it.
In covered, a category's verdict is its strongest tool's, and each tool
has its own with the files it runs in. Tools are listed strongest first.
language appears on a suggested tool only when it is specific to one
language, and location on a weakened category only when it points inside
the repository. Before 0.10.0, --output json printed only the bare array that
is now uncovered, with capitalised keys.
SARIF Output
SARIF is the standard interchange format for static analysis results. Emitting it lets uncovered categories show up in GitHub's Security tab, and in any other SARIF-consuming platform, alongside your other scanners.
Each uncovered category becomes one SARIF rule and one result at warning
level, with the suggested tools listed in the result's help text. Results are
anchored at a CI configuration file — the place the missing tool would be
added. A missing control is not a defect on a particular line, but every SARIF
consumer expects a location, and GitHub code scanning rejects a report whose
results have none.
Markdown Output
Markdown is the format for putting a scan in front of people rather than in a file. Tools link to their repositories, and the report opens with a hidden marker so a workflow can update the comment it posted last time instead of adding a new one on every push.
Sample Output:
When nothing is missing the table is omitted, leaving a single line.
Writing the report to a file
Use --output-file to write the report to a path instead of stdout. This works
with every format, and is the reliable way to produce a machine-readable report:
redirecting stdout captures the debug log as well, so --output sarif --debug > report.sarif yields a file that is not valid JSON.
To publish the report from a GitHub Actions workflow:
Uncovered categories then appear in the repository's Security tab alongside your other scanners.
Posting the report to a pull request
SARIF puts uncovered categories in the Security tab, which people visit occasionally. A pull request comment is where they already are.
--edit-last keeps one comment up to date instead of adding one per push. It
matches on the last comment by the same user, so if something else comments as
github-actions too, match the marker instead:
A pull request from a fork gets a read-only token, so the comment step cannot
post. Guard it with
if: github.event.pull_request.head.repo.full_name == github.repository
rather than reaching for pull_request_target, which would run your workflow
with write access to a fork's code.
Coverage Score
Every scan is summarised as a coverage score: the categories that apply to the repository, minus the ones still uncovered.
Three rules keep the number honest:
-
Excluded categories leave the denominator. A repository is not marked down for a category it has declared irrelevant with
--excluded-categories. -
So do categories with nothing to scan. A repository with no Terraform, Helm chart, Kubernetes manifest or Dockerfile is not told to add Checkov, and is scored out of ten rather than eleven. The scan says which:
Three categories are ruled out this way. IaC Security needs infrastructure to scan, and the detection is deliberately broad — a directory called
infra,k8sorchartsis enough to keep it. Workflow Security needs GitHub Actions or GitLab CI, the formats its tools read. A GitLab project is pointed at glsec or poutine, a GitHub one at zizmor, actionlint, Scorecard or poutine, and a CircleCI-only repository is not scored on it. Agent Skill Security needs aSKILL.mdoutside dependency and test directories; it is credited for Cisco Skill Scanner, SkillSpector or Snyk Agent Scan run in CI. To check the skills themselves, seezanadir skills.The rest apply to any codebase, and the ones that arguably do not — load testing a library, an SBOM for something that ships nothing — cannot be told apart from the ones that do, so guessing would rule out categories a repository does need.
-
Baseline-accepted gaps still count as uncovered. A baseline records that a gap is tolerated, not that it was closed. A score that rose because someone committed a file would measure nothing.
README Badge
--badge writes the score as a shields.io endpoint file:
Publish that file from CI and point a badge at it. Put it on a branch of its own rather than committing it to the default branch: a protected default branch only takes changes through a pull request, so a workflow that pushes the badge there is rejected.
A job that keeps it current on every push to the default branch, publishing
to a badges branch it creates on the first run:
The badges branch holds that one file and nothing else. If a ruleset
covers every branch rather than only the default one, exclude badges from
it.
The colour tracks the score: green at 80% or above, yellow at 60%, orange at 40%, red below that, and bright green only at full coverage.
Effectiveness
Presence is not protection. A scanner wired up so it can never fail a build leaves a repository worse off than one with no scanner at all, because everybody believes the control is there. All of these used to report covered:
A scan now grades each control it finds and reports the ones that protect less than their presence suggests:
A pull_request trigger gates only if it runs on the change itself and on
pull requests to the default branch. types: [closed] runs after the merge,
so it grades like a push. A branches: or branches-ignore: filter that
leaves out the default branch makes the control unknown. The default branch
is set outside the repository: zanadir reads it from the checkout's
origin/HEAD, which git clone sets, from GitHub with --repo or
--required-checks. Where none of those has it, as in most CI checkouts,
main, master, trunk and develop stand in for it, or whichever of them
the filter names.
Supply Chain is graded against a different path. An SBOM, a signature or a
provenance attestation describes what ships rather than looking for something
to block, so it belongs where the artifact is published: running on a release,
a push, a tag, a pull request or a workflow chained after one is enforcing.
A manual run may be the release itself, which the file cannot tell, so it is
unknown. Only a schedule, detached from anything that ships, is advisory,
and continue-on-error is advisory here as everywhere: a signing step that
can fail silently publishes unsigned artifacts.
SCA is also held to what it can see. A dependency scanner looks packages up
by name and version, so one that uv.lock installs from a git repository or a
direct URL is invisible to every scanner, and an enforcing SCA control in a
repository with such a package is partial, with the packages named:
A registry other than PyPI does not count, since a mirror or proxy serves
PyPI's packages under the same names, and path, directory and editable
sources are the repository's own code.
A weakened control does not fail a scan unless you ask it to. --enforce and
--fail-on act only on categories with no tooling; --fail-on-weakened is
what turns a weak control into a failure:
The two are independent. --enforce on its own still ignores weakened
controls, so upgrading does not start failing builds that passed before.
--fail-on narrows both.
A baseline accepts a weakness at the verdict it had when accepted:
So accepting partial does not also accept it becoming advisory later. If
the control gets worse than its recorded verdict, the scan fails again.
--write-baseline records the verdicts for you.
Some of this is generic. continue-on-error defeats any control, and so do
its shell spellings: gitleaks detect || true, || echo failed, and running
the tool as the condition of an if or a !. A || exit 1 that fails again
is fine. So does a workflow that only runs on a schedule. The rest is specific
to one tool, and a rule can say so:
A check is either a regex over the step's shell command or one action input
compared to a value - deliberately not an expression language. Covered today:
gitleaks --exit-code 0, tfsec and checkov --soft-fail /
soft_fail: true, and anchore/scan-action with fail-build: false.
Some tools are the other way round: they exit 0 on findings unless told to
fail. trivy without --exit-code 1 (including trivy-action without
exit-code), grype without --fail-on, kubescape without a severity or compliance
threshold (its old --fail-threshold is ignored by current releases, so it is
unknown),
semgrep scan without --error and trufflehog without --fail only report,
so they are advisory. When the repository has a config file the tool reads
by default, such as trivy.yaml or .grype.yaml, or the step names one with
--config, the file may set it, and the control is unknown:
For actions that take flags through an input, exitsZeroUnless can name it.
The flawfinder action, for example, uses:
Matched shell commands are checked first. If none match and the step matches
by its action, the named input is checked instead. Flawfinder with missing or
empty arguments, or with only --sarif ./, is advisory; --error-level=4
lets the normal triggers decide its grade. An input set by an expression such
as ${{ inputs.args }} is unknown, even if the expression mentions the flag.
zanadir cannot see an exit code set in a workflow's env:, such as
TRIVY_EXIT_CODE, so such a step reads as advisory.
Two deliberate silences. An if: condition is never interpreted, because it
can reference arbitrary context and reading one wrong is worse than admitting
it cannot be read - the control is reported unknown instead. And a category
is judged by its strongest control, so one properly wired scanner keeps the
category quiet however many weak ones sit beside it.
Required checks
A scanner can run on every pull request and still block nothing, because its check is not required to merge. That lives in the repository's rulesets and branch protection, not its files, so it is opt-in:
zanadir reads the default branch's required checks from GitHub (public
repositories need no token; GITHUB_TOKEN or GH_TOKEN is used if set). A
GitHub Actions control that would be enforcing stays so only when its job's
check is required, matrix (build (ubuntu-latest)) and reusable-workflow
(codeql / Analyze) names included; otherwise it is partial. A ruleset
that blocks merges on a tool's code scanning alerts counts too: a scanner that
runs on every pull request and uploads to code scanning is enforcing even
when its own step only reports, such as trivy-action writing SARIF without
exit-code:
It also names settings that look like protection and are not: a ruleset that requires checks but targets no branch or only evaluates, and a required GitHub Actions check that no workflow job reports. Checks other apps report, such as DCO, are never judged. If the settings cannot be read, the scan warns and grades without them. Only GitHub is read; on other platforms the merge settings are still outside what zanadir can see.
Controls declared outside the pipeline
Dependabot and Renovate are detected from their configuration -
.github/dependabot.yml, renovate.json and the other names Renovate
accepts - not from a CI job, because that is not where they live.
They are graded advisory. Both run on the forge's own schedule and open pull
requests; neither can fail a build. So a repository whose only dependency
control is Dependabot is reported as covered and weakened, which is a more
useful answer than either "you have no SCA" or "you are covered":
Platform coverage
GitHub Actions is graded in full. GitLab CI is graded for the two signals that
are unambiguous there - allow_failure: true, which is continue-on-error
under another name, and when: manual, a job that only runs when somebody
asks.
GitLab's include: is followed. A local file (local: or a bare path,
wildcards allowed) is read as more of the same configuration. GitLab's own
templates and CI/CD components cannot be read from the repository, so they are
recognised by name: Jobs/SAST.gitlab-ci.yml or
components/sast/sast@... counts as SAST, and likewise for Secret Detection,
Dependency and Container Scanning, SAST-IaC, License Scanning, Code Quality and
the performance-testing templates. A project: or remote: include is
skipped. A job zanadir cannot read is skipped rather than failing the scan.
GitLab's rules:, only: and except: are deliberately not interpreted.
Whether a branch pipeline gates a merge depends on a project setting that is
not in the repository, so reading them would be guessing. A GitLab job with
neither weakening field is reported unknown, which says nothing rather than
something wrong. CircleCI has no equivalent signal and is not graded.
Bitbucket Pipelines splits its file by trigger, so most of it is graded:
default: also runs on a pull request's source branch, and whether that build
blocks the merge is a merge check configured outside the repository, so it is
left unknown for the same reason as GitLab's branch pipelines. Pipes are read
like GitHub actions: pipe: snyk/snyk-scan counts as Snyk, and
DONT_BREAK_BUILD: "true" grades it advisory.
A Jenkinsfile (and variants beside it such as Jenkinsfile.release) is read
as Groovy tokens, not run. sh, bat and powershell steps are read like
run:, and other steps such as snykSecurity, dependencyCheck,
withSonarQubeEnv or junit like actions, with their named arguments as
inputs. Which branches and pull requests build is configured on the Jenkins
job, not in the file, so a control is unknown unless the file itself
weakens it:
A shared-library step configured only by assignments, such as
mavenBuild { dependencyCheck = 'enabled' }, cannot be read.
Azure Pipelines is read from azure-pipelines.yml (or .yaml) at the
repository root, following template: and extends: references to files in
the same repository. A template in another repository (path@alias) or one
named by an expression cannot be read and is skipped.
With no pr:, an Azure Repos pull request is validated by a branch policy
outside the repository, so the file cannot say whether the pipeline gates it.
succeeded() is ignored because it is the default every step already has. A
step with enabled: false never runs and counts for nothing. Tasks are read
like actions, with inputs: as their inputs, so SnykSecurityScan with
failOnIssues: false grades advisory.
In SARIF a weakened control is a note, pointing at the file that produced
the verdict, while a missing category stays a warning.
Language-Aware Suggestions
Zanadir detects which languages a repository uses and suggests only tools that apply to it — a Go project is not offered ESLint, and a JavaScript project is not offered Pylint. Tools that work regardless of ecosystem (Trivy, Gitleaks, Codecov, and so on) are always suggested.
Detection is based on dependency manifests in the repository root and its immediate subdirectories, so common monorepo layouts work:
node_modules, vendor and other dependency directories are skipped, since
their manifests describe code the project did not write. If no language is
recognised, every tool is suggested, exactly as before.
Advanced Usage
Exclude Specific Categories
Skip certain categories during analysis:
Category names must match the list above. An unrecognized name is rejected with an error rather than being silently ignored.
Enforce Mode
Zanadir provides an --enforce flag to ensure that all CI/CD suggestions are fulfilled. If any suggestion is not met, the CI pipeline will fail. This helps enforce security best practices and compliance in automated workflows.
The category that failed the build is printed to stderr:
Exit codes: 0 when the scan passes, 1 when enforcement fails or the scan
cannot run (an unreadable directory or configuration), and 2 for a command
line zanadir does not understand, such as a mistyped flag. A typo therefore
fails the build instead of passing it with nothing enforced.
Failing on Specific Categories
--enforce is all-or-nothing, which makes it hard to adopt on an existing
repository: a single uncovered category fails every build. Use --fail-on to
enforce only the categories you care about, while the rest stay informational:
--fail-on implies enforcement, so --enforce is not needed alongside it.
Category names are matched case-insensitively.
Baseline
A baseline records the gaps a repository knowingly accepts. Those categories are still reported, but they no longer fail a scan — so enforcement can be switched on before everything is fixed, and only new gaps break the build.
Generate one from the current state:
This writes .zanadir-baseline.yaml (override with --baseline <path>) and
exits successfully:
Then enforce against it. The scan passes while the repository's gaps match the baseline, and fails as soon as a new one appears:
Commit the baseline so the accepted gaps are reviewable, and shrink it over time.
Debug Mode
Get detailed logging information:
Config File
--config (-c) reads flag values from a YAML file, so a team stops
repeating them on every invocation. Each key is the name of the flag it sets:
- A flag given on the command line overrides the same key in the file.
- An unknown key is an error, so a misspelt
exclude-categoriesfails the scan instead of silently scanning without the exclusion. - Relative paths (
baseline,badge,output-file) are resolved against the directory holding the config file, not the directory you run from. zanadir fix -creads the same file and uses onlyexcluded-categoriesanddebug.dirandwrite-baselinestay on the command line: one says what to scan, the other is a one-off action rather than a setting.
Custom Rules
zanadir only recognises the tools in its catalogue, so an in-house scanner
reads as a missing category. Teach it with rules: in the config file, in the
same shape as the built-in rules:
A rule that also matches on Artifact.Name can take unlessPackage, a regex
over the workflow's actions: when one matches, the workflow's steps decide and
its name does not count (unlessPackage: '^acme/scan-action').
An exitsZeroUnless with input requires Job.Package in applyOn; it may
also include Job.Run to grade direct commands. Without input, it requires
Job.Run. The runMatches regex is required in either case.
A command's executable is matched by its unquoted basename: "$RUNNER_TEMP/grant" check
is recognised as grant check. Arguments keep their quoting, and the original
command is retained as evidence. Custom command rules use the same normalization.
Custom rules add to the catalogue and never replace a built-in rule, so an
id that is already taken is an error, as is a regex that does not compile or
an unknown category. A rule that matches counts as coverage and is graded like
any other, so --fail-on-weakened catches acme-scan --report-only too.
zanadir fix uses the same rules to decide what is uncovered.
Complete Example
zanadir fix
A scan tells you what is missing. fix prints the configuration to add:
It prints only; no file is touched. The CI platform is detected from the repository, and snippets are emitted for GitHub Actions, GitLab CI, Bitbucket Pipelines, Azure Pipelines or Jenkins accordingly.
Not every suggested tool has a template, and some templates need a file the
repository may not have: the end-of-life check builds an image from a
Dockerfile, and the load test runs a load-test.js written for the service.
fix emits nothing half-right. A category it cannot fill is listed at the end
with the reason, and with the exclusion to use when the category does not
apply:
Coverage snippets set a floor the test run enforces itself
(pytest --cov-fail-under, go-test-coverage), with no account or token;
set it to today's coverage. Templates live in fixer/templates.yaml and pin
every action to a commit with its release in a comment, so the generated
workflow passes zizmor; images, packages and binaries are pinned to a release.
Adding one is a self-contained contribution.
Generating a workflow
--write generates a standalone .github/workflows/zanadir-suggested.yml
instead of printing:
The categories it could not fill are listed after that, and in a comment at the top of the file.
Existing workflows are never edited, so nothing you wrote can be damaged; the worst case is a file you delete. Re-running overwrites the generated file and produces the same result, and zanadir refuses to overwrite a file at that path that it did not generate.
That standalone job is the trade-off: it repeats checkout and adds its own entry to the checks list, which is noisier than adding the steps to a job you already have. Move them into your own workflow whenever you prefer.
--write currently generates GitHub Actions only. On other platforms, run
without it and paste the snippets.
--excluded-categories works as it does for scan.
zanadir skills
Agent skills (SKILL.md files for Claude Code, Codex and other agents) fail
quietly: a vague description never triggers, a linked script was deleted, two
skills compete for the same requests. skills finds every SKILL.md in a
checkout, or in a GitHub repository without cloning it, and reports them:
The safety rules are shallow on purpose and read only what the agent follows or runs, not reference documents that describe a risky pattern. For a security audit of a skill, use SkillSpector.
--output json gives the findings to other tools, --output sarif to GitHub
code scanning, and --output markdown to a pull request comment; --enforce
exits non-zero when any error is found. The SARIF has its own code scanning
category, zanadir/skills/, so uploading it does not replace a scan's
results. The Markdown lists errors first, at most 50 findings, and starts with
<!-- zanadir-skills-report --> so a workflow can update its last comment.
--home checks the skills agents load for you in every project:
~/.claude/skills (or $CLAUDE_CONFIG_DIR/skills) for Claude Code,
~/.agents/skills and ~/.codex/skills for Codex, and ~/.cursor/skills for
Cursor, which reads the others too. A skill folder that is a symlink is read
where it points, as the agents do, and a skill linked from two places is
checked once. Skills inside installed plugins are not read yet. Dependency, test and .git directories are
skipped. --repo fetches only the skill directories' files, as scan --repo
fetches only CI files, and needs GitHub's file list to find them: a repository
too large for one listing has to be cloned and checked with --dir.
Contributors
Feedback, issues and pull requests are welcome; see CONTRIBUTING.md.
来源:README.md,提交 f3c137d
工具
0版本历史
11- v0.31.0最新Nov 7, 2026
- v0.29.0Nov 5, 2026
- v0.28.0Nov 3, 2026
- v0.27.0Nov 1, 2026
- v0.26.1Oct 31, 2026
- v0.25.0Oct 31, 2026
- v0.24.2Oct 29, 2026
- v0.23.0Oct 26, 2026
- v0.22.0Oct 26, 2026
- v0.21.0Oct 25, 2026
- v0.20.0Oct 23, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分