ci-sentinel π‘οΈ
Security auditor for SEVEN CI ecosystems β GitHub Actions, GitLab CI/CD, Jenkins, CircleCI, Azure Pipelines, Bitbucket Pipelines and Travis CI β finds the supply-chain and injection flaws a YAML linter can't see, before an attacker's pull/merge request runs code with your repository secrets.
Available as an MCP server (for Claude / Cursor / any agent) and a pay-per-call HTTP API (x402 USDC or a prepaid card key).
Give it your .github/workflows/*.yml, .gitlab-ci.yml, Jenkinsfile,
.circleci/config.yml, azure-pipelines.yml, bitbucket-pipelines.yml and/or
.travis.yml (each file is auto-routed to the right analyzer) and get a
CRITICAL / VULNERABLE / RISKY / HARDENED verdict with the exact flaws.
What it catches that a linter can't
β¦and the same depth for GitLab CI/CD (.gitlab-ci.yml)
β¦and five more CI systems, same taint model
β¦plus two deep cross-domain detectors a one-shot agent can't replicate
Each analyzer knows the SAFE variables (SHAs, numeric ids, trusted slugs β
BITBUCKET_COMMIT, TRAVIS_PULL_REQUEST, pipeline.git.revision, β¦) and the
SAFE OIDC trusts (sub pinned to repo:org/repo:ref:refs/heads/main /
:environment:prod with aud set) so it doesn't flag every $VAR or every
trust. Zero false positives on hardened configs.
It is a real static analyzer: a line-aware workflow YAML parser β a model of
triggers/jobs/steps/permissions/actions/outputs/needs β an inter-step/inter-job
taint resolver + permission + supply-chain detectors β a scored verdict with
per-finding file:line, the full taint path, concrete remediation, and a
SARIF 2.1.0 report (with codeFlows) you can upload to GitHub code scanning.
β¦and it doesn't just flag β it FIXES, and it GRADES
MCP server (free)
{
"mcpServers": {
"ci-sentinel": { "command": "npx", "args": ["-y", "ci-sentinel-mcp"] }
}
}
Tool: audit_ci_security. Pass files (a map of workflow filename β YAML) or
source (one workflow). The free tier returns the verdict and how many issues
of each kind were found.
Deep audit (deep: true) β two ways to pay
The deep audit returns every finding with file:line, the full inter-step/
inter-job injection taint path, the transitive action supply-chain graph, a
concrete fix (corrected snippet + unified diff) per finding, an AβF compliance
scorecard with a per-control breakdown, and a SARIF 2.1.0 document with inline
fixes (uploadable to GitHub code scanning via github/codeql-action/upload-sarif).
- π³ Card (Stripe) β buy a prepaid key at
https://ci-sentinel.vercel.app/pro/checkout, then set"env": { "CI_SENTINEL_KEY": "<key>" }in your MCP config. - πͺ x402 (USDC) β AI agents pay per call automatically (USDC on Base), no signup.
HTTP API
POST /audit # free, rate-limited β verdict + per-severity counts
POST /pro/audit # deep β pay-per-call (x402) or Authorization: Bearer <key>
POST /mcp # MCP-over-HTTP (free)
GET /presence # live active sessions (last 5 min)
GET / # this landing page
curl -X POST https://ci-sentinel.vercel.app/audit \
-H 'Content-Type: application/json' \
-d '{"files":{".github/workflows/ci.yml":"name: CI\non: pull_request_target\n..."}}'
Why hosted? (the moat)
The npm package is a thin client: it runs no analysis locally. The parser,
taint engine, action-graph resolver and detector knowledge base run server-side
only, behind the paywall β so the premium material (findings, evidence, taint
paths, remediation) never lands on the caller's machine. A npm pack ships only
dist/mcp.js, dist/mcpServer.js and dist/types.js; the engine is never in the
tarball (verified automatically by npm run test:moat).
How the live feed stays live (the update pipeline)
The threat-intelligence feed is ingested, not hard-coded. A periodic job runs server-side:
- Fetch advisories from two public sources (no paid keys):
- GitHub Security Advisory DB β
GET /advisories?ecosystem=actions(public;GITHUB_TOKENonly raises the rate limit, it is not required). - OSV.dev β the
GitHub Actionsecosystem, queried for the components we already track plus everything GHSA just surfaced (so OSV enriches them).
- GitHub Security Advisory DB β
- Normalize each advisory into a
ThreatRecordβ owner/name split, incident class inferred from the advisory text (tag-rewrite / maintainer-compromise / malicious / CVEβ¦), severity bucketed, the affected version range parsed into a selector, the fixed version captured (so an upgraded pin does not fire). - Merge the rows onto the curated seed (
ingestFeed: union refs, keep the worst severity, widen the affected range, de-dup by identity). - Store the snapshot in Cloudflare KV. Every
/pro/auditreads it back viakvFeedProvider, falling back to the compiled seed if KV is cold β so the detector is never blocked and never worse than the static baseline.
# run the refresh once (live: hits the real public APIs)
node scripts/refreshFeed.mjs
# dry run with no network (produces the seed-only snapshot)
CI_SENTINEL_NO_NET=1 node scripts/refreshFeed.mjs
Schedule it with Vercel Cron ({ "path": "/internal/refresh-feed", "schedule": "0 */6 * * *" }, guarded by FEED_REFRESH_TOKEN) or a scheduled
GitHub Action that runs node scripts/refreshFeed.mjs. GET /feed/status reports
the live snapshot's version, generatedAt and record count β the public freshness
proof. None of this ships in the npm client.
Development
npm install
npm run build # tsc
npm test # engine + parser + taint + SARIF + autofix + compliance + corpus tests (851 checks)
npm run test:moat # lock-proof: tarball ships no engine, deep degrades w/o pay
npm run dev:http # local HTTP server (FORCE_LISTEN)
Heuristic static analysis of the workflow YAML you provide β it cannot see repo-level default token permissions, branch protections or org policy, and does not execute the pipeline. Treat findings as leads to verify.
Source & docs: https://github.com/Baneado98/ci-sentinel Β· MIT