Skip to content

Repository files navigation

machin-secure

A KISS, agent-first security auditor. Not built for humans reading a dashboard — built for an LLM agent (Devin, Claude Code, any coding agent) to run in a loop, pipe, and act on. One ~88 KB static binary compiled from machin/MFL. No Docker, no browser automation, no LLM API client inside the tool itself.

Docs site: https://javimosch.github.io/machin-secure/ · Vision & north star: docs/VISION.md · Changelog: docs/changelog.html

rules.json (the moat) --> secure binary (the engine) --> JSONL findings on stdout
                                                                   |
                                            calling agent reads + reasons (its own LLM)
                                                                   |
                                       secure verdict <id> keep|drop  --> persisted store

Why this exists

A clean-room, deliberately simplified take on usestrix/strix (an AI pentesting agent with Docker sandboxes, Playwright, and an LLM tool-loop driving the scan). The KISS lesson — 15 lines of bash beat a 3,000-line search engine — applies here too: a deterministic regex rule engine over the filesystem finds most of what a heavyweight agent finds, with none of the infrastructure, cost, or attack surface.

What it does NOT do (on purpose)

  • No LLM client. No OpenAI/OpenRouter key, no budget, no network call to a model provider. The agent driving secure already is an LLM — it reads the JSONL, reasons with its own model, and the tool never duplicates that.
  • No Docker sandbox, no browser automation, no exploit execution. Read-only static analysis. It never runs target code and never mutates the target repo.
  • No index, no database, no run journal. The filesystem is scanned fresh every time — freshness over sophistication.
  • No report generation. --hart prints guidance (default instance, publish contract) pointing the agent at hart.intrane.fr — the agent authors and publishes its own HTML report with its own model.

Telemetry

machin-secure collects anonymous usage counts — tool, version, which verb ran, os/arch, and whether it failed. No identity, no arguments, no file paths, no data. The payload is an allow-list (event.schema.json); anything not on it is structurally absent.

  • Disclosed on stderr before the first send. secure telemetry prints the exact next payload — inspectable, not trustable.
  • Disabled by default in CI (CI, GITHUB_ACTIONS, GITLAB_CI, BUILDKITE).
  • Off-switches: MACHIN_SECURE_TELEMETRY=0, DO_NOT_TRACK=1, secure telemetry --off (persisted).
  • Endpoint override: MACHIN_SECURE_TELEMETRY_URL (default: https://feedback.intrane.fr/v1/telemetry).
  • Bounded: 2s timeout, no retry, silent on failure, never changes exit code.

See cli-telemetry-spec for the full protocol.

Community rules — agents helping agents

secure rules lets agents search, install, and submit rules to a shared poche-backed relay. The 1000-rule pack ships with the binary; community rules extend it based on real-world gaps agents encounter during triage.

  • Search-only by default — nothing auto-installs. Agents review before installing.
  • Rules are data (regex + metadata), never code. ReDoS-validated on submit and install.
  • Installed rules go to community-rules.json and are loaded alongside rules.json on every scan. Findings from community rules have community:true in the JSONL output.
  • Override relay via MACHIN_SECURE_RULES_URL and MACHIN_SECURE_RULES_TOKEN.
./secure rules search "goroutine leak"       # search the community relay
./secure rules install <rule-id>             # install a community rule locally
./secure rules submit my-rule.json           # submit a rule to help other agents
./secure rules validate my-rule.json         # schema + ReDoS check (local, no network)
./secure rules list --community              # list installed community rules

Usage

./build.sh                                              # machin encode + build -> ./secure

./secure --target ./some/repo                           # JSONL findings on stdout
./secure --target ./some/repo --summary                 # one JSON summary object
./secure --target ./some/repo --show-all                # include already-dropped findings
./secure --target ./some/repo --hart                    # print guidance for the agent's own hart report
./secure --target ./some/repo --diff                    # scan only working-tree-changed files (fast, CI/pre-commit)
./secure --target ./some/repo --diff-base origin/main   # scan only files changed vs a base ref (PR-scoped CI)
./secure --target ./some/repo --workers 16              # parallel full-audit scan (default: 8 workers)

./secure verdict --target ./some/repo <id> drop --reason "..."   # persist the agent's judgment
./secure verdict --target ./some/repo --stdin           # batch verdicts via JSON Lines

Exit codes: 0 clean · 1 error · 2 high/critical findings present. Pipeable: ./secure --target . | jq 'select(.severity=="critical")'.

CI / GitHub Code Scanning

--sarif emits a SARIF 2.1.0 report (validated against the official schema) that GitHub's Security tab, VS Code's SARIF viewer, and any SARIF consumer read directly. Each rule carries a CWE helpUri, a level (error/warning/note), and a GitHub security-severity (0–10) so findings land in the right priority bucket:

./secure --target . --sarif > machin-secure.sarif

There's a reusable GitHub Action (this repo's action.yml) that downloads the prebuilt secure binary + bundled rules.json from the release and writes the SARIF file. It's a composite action — no Docker, no per-run compiler build, just a ~380 KB fetch then the scan. Drop this into .github/workflows/machin-secure.yml in any repo:

name: machin-secure
on: [push, pull_request]
permissions:
  contents: read
  security-events: write   # required to upload SARIF
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: javimosch/machin-secure@v2
        with:
          target: '.'
      - uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: machin-secure.sarif
          category: machin-secure

The action exits 0 whether or not findings exist (so the SARIF upload always runs); severity gating is left to GitHub code-scanning settings. Inputs: target (default .), rules (default: the bundled rule pack), output (default machin-secure.sarif), args (default: empty — extra CLI flags, e.g. --diff-base origin/main to scan only changed files in a PR).

Releases are cut by pushing a semver tag (v2.0.0); the release workflow builds the binary on ubuntu-22.04 (glibc 2.35, so it runs on both 22.04 and 24.04 runners), bundles it with rules.json, attaches the tarball to the release, and syncs the moving major tag (v2) to the same commit so @v2 always resolves to the latest v2.x. Prefer the Docker image instead? Dockerfile + entrypoint.sh remain as a self-build recipe (docker build -t machin-secure .).

The verdict loop (instead of an LLM filter)

Every finding carries a stable id (sha256(rule|file|line)). Rather than secure calling an LLM to triage its own false positives, the calling agent does it with the model it already has, and persists the call:

./secure --target . | jq -c 'select(.rule=="js-hardcoded-secret")'
# agent reasons: this one is a Vue prop binding, not a secret
./secure verdict --target . <id> drop --reason "Vue prop binding, not a secret"
# future scans suppress it automatically; --show-all brings it back

Same BYOK split as grepapi's /v1/brief: the tool returns structured data, the operator's own LLM does the reasoning.

Rules

rules.json1,000 CWE-tagged detections covering secrets (AWS, GCP, Azure, Stripe, Twilio, Slack, GitHub, GitLab, npm, PyPI, SendGrid, Mailgun, Discord, private keys, connection strings), command injection (eval, exec, subprocess, Runtime.exec), deserialization (pickle, marshal, PHP unserialize, Java ObjectInputStream, XStream, BinaryFormatter, node-serialize), SSTI (Jinja, Mako, Twig, Smarty, EJS, Pug, Handlebars, FreeMarker, Velocity, Thymeleaf), XSS (dangerouslySetInnerHTML, v-html, @html, mark_safe, |raw, innerHTML), SQL injection (raw queries, ORM injection, LIKE/ORDER BY/LIMIT/OFFSET injection), NoSQL injection ($where, $ne, $regex, $in), LDAP injection, template injection, XXE (all major XML parsers), weak crypto (MD2/MD4/MD5/SHA-0/SHA-1, 3DES, RC2/RC4, Blowfish, ECB mode, small RSA/DH keys, PKCS1 v1.5), TLS misconfig (SSLv2/v3, TLSv1.0/1.1, NULL ciphers, export ciphers), SSRF, open redirect, path traversal, CSRF, CORS, mass assignment, ReDoS, log injection, IaC misconfigurations (Terraform, CloudFormation, Ansible, Puppet, Chef, SaltStack, Pulumi, Helm), container/K8s security (Dockerfile, pod securityContext, privileged pods, hostPath, service mesh mTLS), serverless (Lambda, Fargate), CI/CD pipeline security (GitHub Actions, dependency management, SBOM, SAST, secret scanning), microservices/API gateway (gRPC, GraphQL, REST, Istio, circuit breakers, sagas), auth/session security (OAuth, SAML, OIDC, JWT, MFA, session management, password hashing), database deep dives (MongoDB, PostgreSQL, MySQL, Cassandra, Neo4j, Couchbase, SQLite), logging/monitoring/error handling, concurrency/race conditions/DoS, input validation/output encoding, info disclosure, framework rules (Express, Next.js, Nuxt, Angular, Vue, Svelte, Rails, Laravel, Django, Spring Boot, Flask), cloud (AWS, GCP, Azure, Alibaba, Cloudflare), mobile security (Android/iOS), language deep dives (C/C++ format strings, Rust unsafe, Go unsafe/cgo, Java JNDI/SpEL/XPath, C# Roslyn, Kotlin coroutines, Swift keychain), and supply chain) across Python, JS/TS/Vue, Go, Rust, C/C++, C#, Java/Kotlin, Ruby, Shell, Swift, PHP, YAML, Terraform, Dockerfiles, XML, TOML, .properties, plist, INI, requirements.txt, Gemfile, SQL, HTML, CSS. It's plain data, read fresh from disk every run — extend it without recompiling.

Verified against

  • Synthetic fixtures (test/fixtures/) — all expected findings fire.
  • A 10.4k-file production Node.js monorepo — 1,950 findings including a real hardcoded GitHub PAT and private-key material in .env.*.bak files. Full parallel scan (8 workers): 1m28s (down from ~4m37s single-threaded). --diff with 8 changed files: 0.23s. Same 1,950 findings every way.
  • A 36k-file Vue/TS monorepo — full scan in ~13 min pre-parallelization.
  • A fully non-interactive devin -p ... --permission-mode dangerous run: scan → read hint → author HTML report → publish to hart.intrane.fr, with zero manual steps.

See AGENTS.md for design rationale, MFL gotchas, and internals.

License

MIT

About

Agent-first, KISS security auditor in MFL/machin — no LLM client, no sandbox, no report generation baked in. Clean-room inspired by usestrix/strix.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages