Repository navigation
·
29 commits
to main
since this release
Immutable
release. Only release title and notes can be modified.
First public release of mcpmoat, an offline scanner for malicious or tampered MCP servers.
This is an early release. Expect rough edges. Read "Not done yet" before you rely on it.
What works
- Discovery.
mcpmoat scanfinds the MCP servers in your Claude Code, Cursor and Codex configs. It shows the origin, type and command of each server. It masks secret-like arguments. - Config checks.
mcpmoat scan --no-connectchecks the configs only. It starts no server and opens no connection. It flags a plaintext secret, a remote server over plainhttp://, an unpinned package and a typosquatted package name (threats T4 and T5). - Inspection.
mcpmoat scanconnects to each stdio and HTTP server and lists its tools, prompts and resources. A stdio server runs with exactly the configured command and a scrubbed environment. Each server has a time limit (--timeout). A server that fails does not stop the scan. - Rug pull detection. The first
scanpins the tools of each server inpins.json. A laterscanreports a changed, new, removed or unverifiable tool (MOAT-T2-001toMOAT-T2-004) with a diff. Accept a change withmcpmoat pin --accept <server> --expect <digest>. - Output and exit codes. Terminal report, or one JSON document with
--json(schema indocs/schema/). Exit code 0 means no findings, 1 means findings at or above--fail-on(defaulthigh), 2 means an execution error. - Ignore lists.
--ignore <rule-id>and a.mcpmoat-ignorefile. - Signed builds. Archives for macOS and Linux (amd64 and arm64), with
checksums.txtand build provenance attestations.
Not done yet
- The tool rules for tool poisoning (T1) and shadowing (T3) do not run yet. So exit code 0 does not mean that the tools are safe.
scan --rules <dir>loads and checks extra rule files, but does not apply them yet.- There is no lock for
pins.jsonyet (#150). - The macOS binaries are not notarized yet. The Homebrew cask removes the quarantine flag on install.
- The env scrub is not a sandbox. A stdio server runs as you.
- mcpmoat does not analyse server source code. It does not stop prompt injection in content that your agent reads.
Install
brew install --cask Ducarv/tap/mcpmoatOr download an archive below, or run go install github.com/Ducarv/mcpmoat/cmd/mcpmoat@v0.1.0. Check a download with the steps in Verify a download. Then run mcpmoat version.
Changelog
- 503d5ba Merge pull request #183 from Ducarv/chore/166-167-e2e-test-helpers
- e8207f9 test(cli): share the exec runner and tidy helper comments
- f660759 test(cli): apply review fixes to the shared helpers and the exit-code test
- 9c2f76c test(cli): share pins helpers and cover the rugpull fixture exit codes
- cae8832 Merge pull request #176 from Ducarv/chore/66-release-pipeline
- 5b4d0d6 docs(release): cover a failed post-publish check and bind verify to the commit
- 43702e4 fix(release): name a missing dist file and fix the 12-character commit note
- 1c5d652 Merge origin/main into chore/66-release-pipeline
- 0eec53f Merge pull request #165 from Ducarv/feat/62-rugpull-fixture
- 9902694 docs(release): close the cask checklist against Ruby interpolation
- 37e9454 ci: read both tool pins from env lines with one anchored parser
- 3e6f7a4 fix(release): fail when the published release is not immutable
- 75917f3 Merge origin/main into feat/62-rugpull-fixture
- fc56e63 Merge pull request #179 from Ducarv/feat/63-terminal-report
- 9de4b40 Merge origin/main into feat/63-terminal-report
- 3de2012 docs(release): match the cask checklist to a real cask
- d1ea553 docs(release): check the whole cask, not only its sha256 values
- 470b31f ci: check the govulncheck pin too, and loop the static check over targets
- f36cf8a fix(release): accept only first-parent main commits and recheck the tag after publish
- 55cd693 Merge pull request #177 from Ducarv/feat/65-exit-codes-fail-on
- ccae74b fix(cli): give the size-limit MOAT-T2-004 row of a pinned server its own risk
- c65da69 Merge origin/main into feat/65-exit-codes-fail-on
- ee8267a Merge pull request #173 from Ducarv/feat/64-json-output
- d9e3174 fix(report): print the failure reason after the counts in the scan summary
- c76e273 docs(release): state the secret and ordering limits, reword NFR-02
- 2ce0bae ci: match every GoReleaser pin form and send script errors to stderr
- d7104d9 fix(release): verify the exact signing workflow and recheck the tag
- ada13ac docs: say that the signal check before the pin save applies only to new pins
- 5d9d2cd fix(cli): fail a pinned server whose tools no longer fit the pins size limit
- 5d5309a docs: pin advice for go install, symlink-safe hook install, one exit 2 list
- e9f10f7 refactor(cli): say "not ignored" in the exit code help and link the README contract
- a01b3f5 docs(release): drop the manual publish, order the setup, fix install notes
- c3f2e16 ci: move the snapshot check to a script and check the GoReleaser pins
- c6cc3af fix(release): stop on a missing environment and recheck assets after publish
- 2eedd09 fix(release): pin archive tar headers and skip rc tags for stable changelogs
- 86c94f0 test(cli): drop a blank line left by the TestScanJSONDiffKinds move
- 3d694fa fix(cli): give every pin row of a server with unpinnable tools a fix that works
- cc2f4ab docs: describe the --fail-on critical note and a pins.json read error on a signal
- a037d96 feat(cli): note on stderr for --fail-on critical and add severityFlag
- d92e4da test(report): check the E2E JSON against the schema and tidy round 3
- 7abdc94 docs(report): state the scan --json contract edges and tidy the JSON code
- ff228b1 fix(report): print the reason of a server-level pin finding as Reason, not Tool
- 41eb880 docs: make README the one exit code contract and harden the CI guide
- 825bbce test(cli): use perLevel tables and copy the CI fixture with os.CopyFS
- af2a507 refactor(cli): shorten the exit code usage text and point to README
- 126e30c fix(report): flag incomplete reports and diff notes in scan --json
- 904be83 fix(release): reproducible archives, full asset check, brew verify docs
- b24ef50 fix(report): give scan-made MOAT-T2-004 rows a fix that works, match outcomes by index
- 56d955d feat(report): add the terminal report with summary, severity groups and colour
- 1f7e7b2 fix(release): pin the current tag, replace stale drafts, dedupe verify docs
- 2498e69 docs: pin the CI install, guard .mcpmoat-ignore and fix hook edge cases
- 6d9d834 refactor(cli): narrow the exit 2 usage text and trim --fail-on tests
- 696d4b7 refactor(report): type the JSON mode and inspection status, clarify docs
- 5cb0f76 docs(release): document install, verification and the release process
- c34c0a3 ci: check the GoReleaser config and build a snapshot on every PR
- 81d0eb5 chore(release): add GoReleaser config and attested release workflow
- d132be0 feat(report): add scan --json with a versioned JSON Schema
- 0fc02f2 docs: document exit codes and add the CI and git hook guide
- 29e8d07 test(cli): add a CI fixture repo and TestE2EScanFailOn
- 0012293 feat(cli): add scan --fail-on to set the exit code 1 threshold
- 7c436eb test(pins): tidy TestE2ERugPull after the round 1 review
- f51b670 Merge pull request #164 from Ducarv/feat/153-hash-meta-icons
- 57257e9 docs(pins): document inline icon size and the new-tool diff cut, drop copied limits
- af9b9a3 test(pins): finish the marshalJSON and canonicalJSON renames in test names
- fdfeb8a docs(pins): note server URLs in pins.json and match the render field order
- feb14f2 test(cli): check that a pinned server keeps pins.json after a bad _meta
- baf69b2 refactor(inspect): rename marshalSchema to marshalJSON
- a08f01f test(pins): remove test duplication around _meta and icons
- 7baa36e docs(pins): correct the render order note and the indented field count
- f38f69a fix(inspect): name the _meta field by its wire name in the encode error
- 2df6bdc fix(pins): reject a _meta that is not an object and icons that are not an array
- 79f38b2 test(pins): apply review fixes to the rug-pull fixture
- a0403c4 refactor(pins): tidy the _meta and icons code after review
- b73f36f test(pins): add the rug-pull fixture and an E2E journey
- 1c360b6 feat(pins): hash tool _meta and icons in the pin hash
- f660389 Merge pull request #159 from Ducarv/feat/60-61-pin-diff-accept
- 0bfaaf1 fix(ci): run the E2E pin tests below a private TMPDIR
- 9ee80a3 Merge remote-tracking branch 'origin/main' into feat/60-61-pin-diff-accept
- bae9265 test(inspect): check ctx, not a wall-clock bound, in the flood stop test
- 5c2e052 fix(pins): treat an unverifiable new name as a rename, and tidy the hint code
- 7fc8f3d feat(pins): add pin --project, show inspection warnings, and flag renames
- 4b1eeed fix(pins): fail on a server that cannot be pinned, and stop the save on a late signal
- 99ade3b fix(pins): give no accept command for a cut diff, and cap render indentation
- 87cb40f fix(pins): bind the accept hint to what scan shows, and keep the full digest
- b6804fa fix(pins): fail on a pinned server that was not inspected
- 751840d fix(pins): show new tools, warn on cut diffs, and split the pin code
- 8a7753b feat(pins): detect tool changes with a diff and add pin --accept
- c2bc39c Merge pull request #155 from Ducarv/feat/58-canonical-hash
- 5e887d3 chore(ci): re-run the GitGuardian check after the false positive triage
- 24ed921 Merge origin/main into feat/58-canonical-hash
- 9234872 fix(pins): state the hash error and empty schema rules, and rename test helpers
- 3a33e7f Merge pull request #151 from Ducarv/feat/59-pins-store
- 9d8d45b fix(pins): never accept a tool silently on a hash version change
- ad6060f fix(pins): check a schema before its numbers, and tidy the hash tests
- 851c8d4 fix(pins): refuse a dangling parent of the pins directory as a first run
- 53849ef fix(pins): refuse a dangling pins directory and apply the server cap in Load
- 4eb706e Merge pull request #148 from Ducarv/feat/139-140-lookalike-scripts
- d90ce87 fix(pins): require every pins.json key and refuse a dangling symlink
- 8d69ec3 fix(pins): correct the number limits and tidy the hash tests
- c9fb0bb fix(pins): cap each server in Set and decode pins.json with encoding/json/v2
- 899594d fix(pins): apply review findings to the canonical tool hash
- ac18e54 test(rules): keep modifier letters out of edgeLookalikes
- a7a573b fix(rules): do not count modifier letters in an edge run, and speed up skeletonOf
- 0df57ab feat(pins): add canonical form and versioned SHA-256 tool hash
- c905060 fix(pins): guard a zero-value Store and tidy the write cleanup
- e1e4cd5 feat(pins): add the pins.json store with a versioned format and atomic writes
- 40b3b34 fix(rules): excuse Zawgyi marks that do not read as Latin letters
- 749b2cf fix(rules): close edge-tier look-alike gaps and tidy the look-alike code
- c93f168 feat(rules): fold look-alike letters of other scripts in regex forms
- 38351f1 feat(rules): count look-alike scripts outside the mixed_script list
- 4f2d7d9 Merge pull request #145 from Ducarv/feat/133-match-forms-once
- 774a948 fix(rules): document the ZWSP-in-CSI limit and correct the hasSeqC0 note
- 00d30fb fix(rules): bound the OSC regex of the new forms and count empty OSCs in the window
- 29b939e fix(rules): count CSI text in the match window and place run spaces before ESC
- c9d88d9 docs(rules): point the scan engine notes to issue #143
- 5d92ebd fix(rules): address review round 2 for the sequence forms
- cf9bacc Merge origin/main into feat/133-match-forms-once
- ebbad7a fix(rules): close the review gaps in the sequence forms
- fdd977d Merge pull request #137 from Ducarv/feat/118-t1-gaps-schema-window-markup
- 56dbfdd Merge pull request #142 from Ducarv/chore/141-gitguardian-ignore
- adc32e7 fix(ci): drop the root anchor from the GitGuardian rules glob
- cbf3c46 Merge origin/main into feat/118-t1-gaps-schema-window-markup
- 7a8df5a fix(ci): match nested testdata fixtures in GitGuardian ignores
- 12cc6e8 chore(rules): renumber MOAT-T1-010..020 to 012..022
- 8e51bfd chore(ci): ignore test paths in GitGuardian scans
- 314428d Merge pull request #136 from Ducarv/feat/131-mixed-script
- 51d4619 Merge origin/main into feat/118-t1-gaps-schema-window-markup
- afdc85d fix(rules): let U+FFFD join a mixed_script word and check before formatting
- 7e35cc1 fix(rules): close two mixed_script bypasses
- ddfb8f1 refactor(rules): parse the hex of a JSON escape with the package helpers
- ac8af7b fix(rules): treat JSON white space like json.Valid and remove Match.Compile
- 3f5d536 fix(rules): excuse only real units in mixed_script
- 45c7b54 Merge pull request #135 from Ducarv/feat/132-t1-005-noun-corpus
- 46edaec fix(rules): read base64 JSON strings with json.Decoder at any depth
- 93514c3 Merge pull request #134 from Ducarv/feat/39-57-config-rules-fixtures
- 9b2745e fix(rules): flag mixed_script words with no Latin letter or a private-use rune
- 7f8eedd fix(rules): read deeply nested base64 JSON as plain text for min_words
- 3ba986e docs: say that malicious exfil corpus cases are synthetic
- e9feae3 fix(rules): require a separator after the MOAT-T1-005 wording noun
- de3f0e2 fix(cli): say on stderr that --rules rules are not applied
- 6a969d9 test(rules): walk every fixture Go file and state the import check limit
- 7c7ac62 fix(report): quote a findings cell that is exactly "-"
- b0030c4 feat(rules): build match forms once per text and add three forms
- 8d0c291 test(rules): fail when a schema copy joins two heuristic sources
- 4ea59bf test(rules): pin the MOAT-T1-005 "the URL:" form that the merged wording now catches
- 1670d66 fix(rules): count long sentences in base64 JSON and fix abrupt comment close
- 223238b feat(rules): close MOAT-T1-005 wording gaps and check its two prefixes agree
- ec77ee4 fix(rules): skip JSON in base64 min_words and correct the seam comments
- 65ffbba fix(rules): address T1 review findings and match neighbour schema strings
- 3025076 fix(rules): flag Latin mixed with other look-alike scripts
- b71f945 fix(rules): close mixed_script bypasses found in review
- 1233d36 feat(rules): decode schema text, add match window and hidden-text rules
- 02307d4 feat(rules): tune MOAT-T1-005 nouns with a benign description corpus
- 1434dc6 test(cli): cover the fail-closed severity and exit checks
- 977c51e docs: describe config findings in scan and the benign fixture
- 60aae1f feat(cli): run the config rules with scan --no-connect
- e141b51 feat(report): add WriteFindings for the config findings table
- 43236e5 feat(rules): add NewBuiltinTyposquat and check fixture imports
- 165d19b feat(rules): flag mixed-script words that evade the T1 regexes
- 94921fe Merge pull request #129 from Ducarv/feat/112-116-t4-config-checks
- 0a297fd Merge pull request #130 from Ducarv/feat/118-t1-detection-gaps
- 33fab5d fix(rules): rename isNameSeparator in argsecrets.go
- d3c77c2 docs(rules): say how a format rune after ESC defeats the regex forms
- 509a364 docs(rules): state the two-space run and the format rune after ESC limit
- c192926 fix(rules): follow npm for aliases, scp specs and hosted URLs in T4-003
- a2be6fb fix(rules): close two npm pin bypasses in T4-003 and more T4-002 false positives
- 2ee5bf8 docs(rules): say that TestCheckArgSecretsWordURLs runs five places
- fc790b5 fix(rules): report pipx script URLs as pipx reads them, fewer T4-002 false positives
- bedd6a7 test(rules): name the six-forms text and the control padding size
- 9d16306 fix(rules): keep a space after a mixed invisible run and stop the JSON CSI at a backslash
- a38f815 fix(rules): stop false T4-002/T4-003 findings from the first review fixes
- bd42952 fix(rules): find header flag secrets, password args and pinned hosted git URLs
- 7cf2b02 fix(rules): drop leftover ANSI controls and space control runs once
- ff3e72c fix(rules): keep main's control-rune form and stop fold padding
- 48a964d fix(rules): stop readable names and padded secrets fooling T4-002
- b1f51d5 Merge pull request #124 from Ducarv/feat/115-t3-schema-text
- 885d517 fix(rules): always scan the T3 description and skip only plain own names
- 1166cec Merge pull request #125 from Ducarv/feat/113-t5-threshold-corpus
- 9cc1ffc docs(rules): correct the T5 threshold examples and the PRD wording
- ed79f63 fix(rules): skip only a one-token own-name text in the T3 check
- 388f393 Merge pull request #123 from Ducarv/chore/114-shared-safe-read
- 072a9a2 test(rules): wrap the corpus scanner error
- b94fb3a docs(rules): record both T5 short-word trade-offs and state the threshold positively
- 06fc47b docs: state the safefile error and maxSize contract exactly
- e34dcac test(rules): scan the typosquat corpus file without a string copy
- 04b8137 fix(rules): keep two T5 edits for runes added at the end of a popular name
- 2beca29 feat(rules): fold regex input and treat control runes as invisible
- affdefc docs: list the annotations title in the T3 detection text
- 46306ce fix(rules): skip T3 tool text that is only the tool's own name
- 6f6fa7a refactor: route every untrusted root open through safefile.OpenRoot
- 0b2d571 feat(rules): report secrets in args and URLs, git specs as unpinned
- ee63712 docs(docs): state the T5 edit-distance threshold in the threat model
- 5c2ba68 fix: do not block when the safefile directory is a FIFO
- 679a7e9 docs(rules): describe the T5 tail as the part after the shared separator
- 81715a6 refactor(rules): tie schema caps to inspect and flatten the T3 scan loop
- 8293cbe refactor(inspect): export the JSON depth and value caps
- be286e6 fix(rules): measure the T5 tail from the last word separator
- 9e74d9f feat(rules): base the T5 two-edit threshold on the distinctive tail
- 04a2773 docs: widen the T3 detection text in the threat model
- e4614e0 feat(rules): scan tool titles and schema text for T3 shadowing
- f7e8c96 refactor: share the hardened file reader in internal/safefile
- f9e1fac Merge pull request #121 from Ducarv/docs/120-docs-single-source
- 089f3dd docs: apply the security exception in every docs gate
- 5a12789 docs: define a real "no doc change" reason once and close the bypass
- 747be50 docs: make the pr skill the one docs gate and back the security-flag rule
- f136513 docs: keep one docs finding source and restore the exact secrets rule
- 88de5fb docs: fix the docs-check flow and restore the always-loaded security rules
- 71dc288 docs: give each topic one owner doc and add an architecture page
- e0ccbcb Merge pull request #119 from Ducarv/feat/51-t3-t4-t5-rules
- f688a0b docs: list the config heuristics and their target in CONTRIBUTING.md
- f42918f Merge origin/main: add the T1 rules next to the T3, T4 and T5 rules
- dfd0030 refactor(rules): keep the MOAT-T3-001 patterns in step and tidy shared helpers
- 75e9670 fix(rules): read --qy style shorthand groups and close two npx edges in MOAT-T4-004
- 5868a48 Merge pull request #117 from Ducarv/feat/42-t1-rules
- fcbe505 fix(rules): read "_" in header names and skip URLs in the plaintext_secret adapter
- 809f342 fix(rules): stop MOAT-T3-001 from reading GITHUB_TOKEN or macOS as tool names
- 243a31c fix(rules): fail closed on npx flag arity in MOAT-T4-004
- b52ab97 test(rules): check the MOAT-T1-009 copy in both directions
- 8f7435b fix(rules): narrow the keep pattern and make the URL-safe case real
- cf80210 fix(rules): read ANSI escape sequences as one space before matching
- 2f8cba2 test(rules): check that MOAT-T1-009 stays in sync with its source rules
- d1ff705 fix(rules): close the round 4 gaps in MOAT-T1-001, 003 and 009
- aa284a7 fix(rules): read control characters between words as plain spaces
- e8a27e7 fix(rules): require target config for config heuristics
- 64e3791 fix(rules): stop MOAT-T3-001 from reading hyphenated words as tool names
- 9fda43f fix(inspect): match npm_config_yes with ASCII case folding only
- 0dee533 fix(rules): follow nopt for yes values and no- prefixes in MOAT-T4-004
- 9f460ae test(rules): require ASCII built-in rule files
- f7ca679 fix(rules): cut new T1 false positives and make two cases test their claim
- e17b502 fix(rules): read npx yes flags as npx 11 does and correct MOAT-T4-004 advice
- 7ec6034 fix(rules): match whenever, before, after and "the X tool" in MOAT-T3-001
- 2528aaf test(rules): check the T1 rule IDs in their own test
- 4b0b21f fix(rules): close the round 2 gaps in the T1 patterns
- 77ac12b fix(rules): map all white space that RE2 \s misses, as the keywords do
- ac6cb51 test(rules): assert the T1 hits of the shadowing fixture
- f67ab97 fix(rules): harden the T1 patterns against white space, escapes and drift
- fb942a3 fix(rules): read Unicode space separators as plain spaces before matching
- 2eac473 feat(rules): add built-in rules MOAT-T3-001, T4-001 to T4-004, T5-001
- 71b89b7 feat(rules): add the T1 tool-poisoning rules MOAT-T1-001 to MOAT-T1-009
- ded478b fix(ci): bump the Go toolchain to go1.27.3 for GO-2026-6809
- c773211 Merge pull request #109 from Ducarv/feat/37-t3-t5-heuristics
- 4bfd2ce Merge pull request #110 from Ducarv/feat/35-rules-matchers-ignore
- 901a87a Merge branch 'main' into feat/35-rules-matchers-ignore
- 5e3692c Merge pull request #111 from Ducarv/feat/33-rules-engine-core
- 35f6671 fix(rules): close T3 emphasis bypass and self-shadowing of local binaries
- 4bc01f4 docs(rules): say that explicit tags are errors and ID clashes ignore case
- d1506b2 test(cli): share the terminal-safe check and the reason text constants
- 4628eba fix(rules): refuse explicit YAML tags, ignore case in built-in ID clashes
- 0bf225d test(fixtures): point the T4 and T5 config fixtures at .invalid names
- 048723c fix(rules): keep word separators in the T3 fold and fold T5 server names
- 3b6ac08 docs: say that FR-08 lists the fields of a built-in rule, note the folder symlink
- c86736d test(cli): share scan modes and helpers, drop duplicate tests, wrap --rules help
- 58b237d fix(rules): keep the first errors in file order for match.params
- 9e9d4d4 docs: note the --no-connect fixtures and the new termsafe consumer
- a578fd3 fix(rules): key T3 identity on the runner flags and fail T4-001 closed
- 5b14a43 docs: say that only built-in rules must carry test cases
- 5cc9990 docs(rules): say that an empty rules folder is an error and params keys are checked
- 517deda fix(cli): exit 2 for a --rules folder with no rule files, share scan test helpers
- b7a479a fix(rules): fail on a folder with no rule files and check match.params
- d2fa5bd refactor(cli): name scanWarnf and table-drive the config-mode ignore tests
- 3f0ba84 fix(rules): one space per invisible run, NFKC for keywords
- 8c3626f refactor(cli): add a scan warnf helper and share the notice check
- 01141b2 refactor(rules): name the keyword regex classes and the no-offset value
- fff2f68 fix(rules): give a malformed line after the entry cap its own reason
- acd6def fix(rules): match a second form where invisible runes are spaces
- ecb8377 fix(rules): cut T3 and T5 false positives and close T3/T4 gaps
- 6883918 docs(rules): list all rule limits and say how hidden files are handled
- 14888d7 test(cli): share the flood time guard and cap, print both baseline sides
- 2fe3e59 fix(rules): refuse a non-directory --rules path and bound the folder listing
- d2cf494 refactor(cli): move shared test helpers and point the ignore list to #40
- edfea73 refactor(rules): one compile loop for regex and keyword lists
- 265cece fix(rules): fold all combining marks for keywords, document regex limits
- b7f3ffa fix(rules): split ignore lines only on space and tab, refuse hidden runes
- ae6bd63 fix(rules): tighten the T3, T4 and T5 heuristics
- 29432af test(fixtures): drop the unused -mode flag from the T1 and T3 servers
- d5e5dd2 refactor(rules): keep caseErrors next to the errCase values, reuse fakeMatcher
- 376255d test(cli): share the ignore-test helpers and the usage hint
- 6a6cebd docs: note the lenient rule loader in FR-08, drop a research note
- bc33573 docs(cli): describe --rules in the runScan doc and tidy scan tests
- 0b68400 fix(rules): count the cut file and open rule files without blocking
- d6076f7 refactor(rules): share the instruction count and the test helpers
- c88cc85 refactor(rules): wrap the ignore read error once and lower the entry cap
- ec5a371 fix(rules): fold marks and the braille blank in keyword matching
- 8104c2a fix(rules): excuse emoji outside So and a ZWJ only between emoji
- 2a6b751 docs(rules): document the final rule file format
- 1b1d0e0 feat(cli): add scan --rules to load extra rule files
- 1bcf39d feat(rules): add the rule schema, YAML loader and test harness
- 5166e83 docs(docs): compare mcpmoat with five related tools
- 6f33927 feat(rules): add safe matchers, native heuristics and the ignore list
- 2fe206d feat(rules): add native T3, T4 and T5 checks and malicious fixtures
- 31be45b Merge pull request #107 from Ducarv/feat/87-inspect-launch-policy
- a7c77ce fix(inspect): refuse the workspace folder for untrusted remote servers
- c9b6f09 docs(discover): correct the globalFile and canceled-error docs
- 61264a6 fix(inspect): keep the error chain in showDiscoveredURL and restrict env by trust only
- f78cb8b fix(discover): apply the WorkDir state to local-scope servers of both keys
- 52cca49 fix(inspect): expand only base variables for untrusted servers too
- ccda1d5 fix(inspect): expand only base variables for servers that are not enabled
- e1f744f fix(discover): fail closed for .mcp.json servers read without settings
- 4a82e4e feat(inspect): gate server state, expand placeholders, harden launch and DNS
- a817e9b Merge pull request #105 from Ducarv/feat/98-inspect-hardening
- 1d99b29 docs(inspect): say where requestRecorder.finish is best effort
- 3bfc630 fix(inspect): dial loopback only for every case of localhost, and keep session DELETE out of all records
- 0929587 fix(inspect): warn for a mixed-case LOCALHOST host, which Go sends through a proxy
- 1250e36 fix(inspect): keep connect error labels against a redirected session DELETE
- 70a12a1 refactor(inspect): move HTTP call records and body-shape selection out of httpclient.go
- caedc14 feat(inspect): harden remote and stdio inspection
- 958884f Merge pull request #96 from Ducarv/feat/25-inspect-stdio
- 9fe343f Merge origin/main: build the stdio inspector on main's inspect API
- fa96479 test(inspect): build the flood fixture before the timer starts
- b8417e3 fix(inspect): stop the server before closing the MCP session
- c223315 Merge pull request #95 from Ducarv/feat/26-http-transport
- 4a6ada7 test(inspect): give two multi-step deadline tests room on a busy machine
- ec6dff1 Merge origin/main into feat/26-http-transport
- 53f0dfe Merge origin/main: run stdio servers through stdioCommand
- 888732d Merge pull request #93 from Ducarv/feat/28-timeout-concurrency
- 16554e6 Merge branch 'main' into feat/28-timeout-concurrency
- 1f101d8 fix(inspect): run servers without a Dir below a private cache dir
- ab9a25b Merge pull request #94 from Ducarv/feat/27-scrubbed-env
- c8ac8ee fix(inspect): refuse URLs that cannot be redacted and scrub Basic headers
- f025c67 fix(inspect): scrub query keys and matrix parts, keep timeouts in blocked errors
- 2837be0 fix(inspect): fail on SDK transport errors for undeclared lists
- 7918eca fix(inspect): scrub bare tokens and label timeouts from the SDK correctly
- f5999fb fix(inspect): read the server context first and document the Cancel skip
- 03e654b refactor(cli): move the scan option check into connectOptions
- fd81f8a fix(inspect): guard the group kill after Wait and stop group members on close
- feb7893 fix(inspect): measure raw list pages without HTML escapes
- efbe08b fix(inspect): close idle connections and stop naming mcpmoat to servers
- d58f3bd chore(inspect): update golang.org/x/sys to v0.44.0
- 9f33dc9 test(inspect): share cross-file test helpers and drop duplicate cases
- b56509c refactor(cli): check scan flags with Options.Validate and share usageError
- a3bd084 fix(inspect): give every server a Status when Inspect calls runtime.Goexit
- c7ca14e fix(inspect): keep Setsid in the process-group setup and unblock pipe reads
- a6b665e fix(inspect): tell an empty advertised kind from a missing one
- 2e04fd8 fix(inspect): skip parent env values with a NUL byte and tidy buildEnv
- 2eb31ca feat(inspect): inspect stdio MCP servers behind an SDK-free interface
- 946ebe9 feat(inspect): connect to remote MCP servers over HTTP and SSE
- c7d9838 feat(inspect): build stdio commands with a scrubbed environment
- 53be047 feat(inspect): run servers with a per-server timeout and bounded concurrency
- ce1c622 Merge pull request #92 from Ducarv/feat/21-scan-no-connect
- c501744 Merge pull request #91 from Ducarv/chore/e2e-os-exec-docs
- 3ae73ef Merge pull request #83 from Ducarv/feat/16-server-model
- f5c6ae5 docs: describe the os/exec e2e tests in the profile and agents
- 6f568a6 fix(discover): refuse ".." in an untrusted Codex cwd
- 51dab9a fix(cli): read --config the same way for every path spelling
- 5403b5d fix(discover): keep untrusted Codex servers inside their project
- 15a107d docs: add internal/termsafe to the package layout
- ae6e8fd fix(cli): say that a list-only scan applies no rules and mark untrusted servers
- 57d3450 fix(cli): report unread --config projects and read the user's own file once
- 59b396c Merge remote-tracking branch 'origin/feat/17-discovery-readers' into feat/21-scan-no-connect
- e996c65 fix(discover): fail closed on unknown scopes and Claude Code url-only entries
- cafcac9 fix(cli): fail the scan on any config error and read --config files as untrusted
- 1ed80c5 fix(report): escape blank runes and combining marks as FileError does
- ac154b2 Merge branch 'feat/17-discovery-readers' into feat/21-scan-no-connect
- 61203f3 fix(discover): refuse Codex transport mixes and build errors in one place
- 261efcf fix(discover): refuse null entry values and keep untrusted marks complete
- 95cb314 fix(cli): adapt scan to the masked Args and untrusted project files
- 87fa3ec Merge branch 'feat/17-discovery-readers' into feat/21-scan-no-connect
- cea3d2d fix(discover): confine and mark untrusted project files, tighten readers
- fec87e8 test(discover): share file, error and server helpers across reader tests
- b3a6712 feat(discover): add Server.Dir, FileError.ProjectPath and new reasons
- a3f2b47 fix(discover): read Codex keys exactly and keep the server cwd
- b029783 fix(discover): read JSON keys exactly and reject lone surrogates
- 29ce40e Merge branch 'feat/16-server-model' into feat/17-discovery-readers
- e5547b6 test(discover): share text-marshal adapter and drop canary aliases
- 6966bff fix(discover): allowlist URL schemes and check query keys like segments
- ab8fd82 fix(discover): mask padded base64, numbered names and loose versions
- b6a6a4f test(discover): add an args canary and share the printable-ASCII check
- 60b85e9 fix(discover): check unescaped text and key=value segments in RedactURL
- 7403ee7 fix(discover): close RedactArgs leaks for long flags, glued values and "@"
- cff97f1 fix(discover): fail closed on any "@" after the host in RedactURL
- ad94c96 fix(discover): show args through an allowlist that fails closed
- 52bd856 feat(discover): mask Server.Args by type with a new Args type
- 51fa522 test(discover): share render helpers and fix the http fixture
- 5320940 fix(discover): mask path credentials and cut authorities in RedactURL
- 9d76950 fix(discover): close RedactArgs gaps found in review
- f80f875 chore(discover): allow discovertest only in tests, drop dead guards
- 9835dc8 fix(discover): pass fmt verbs and flags to masked and redacted text
- f577a16 feat(discover): add RedactArgs and make RedactURL output printable ASCII
- 53cff21 feat(cli): list servers with scan --no-connect and --config
- ad926f4 feat(discover): read Claude Code, Cursor and Codex configs
- 6ee90c0 feat(discover): add Server model and secret masking types
- b98ff02 Merge pull request #82 from Ducarv/chore/1-m1-skeleton
- aeaf1d2 ci: add a Dependabot cooldown and clarify the setup-go cache comment
- 48b6bd3 refactor(cli): extract parseGlobals and share the no-arguments check
- 92e5c24 chore: set up Go module, CLI skeleton and CI (M1)
- 4091e6f chore(claude): port the full Nodeglade agent and skill set to Go
- 7a82614 chore: bootstrap repository with PRD and project docs