Typosquatting Risk in Dependencies
Server depends on 'expresss' (triple s) with Levenshtein distance 1 from 'express'
How this rule decides. Each strategy below is a deterministic analysis the detector runs against the MCP server's static metadata, source code, and (when present) live connection handshake.
similarity- 1
Legitimate Fork Allowlist
legitimate-fork-allowlist - 2
Visual Confusable Replay
visual-confusable-replay - 3
Scope Squat Detection
scope-squat-detection - 4
Numeric Version Suffix Strip
numeric-version-suffix-strip - 5
Algorithm Agreement Gate
algorithm-agreement-gate
What we found. Each finding below carries a structured proof chain from source (where untrusted data enters) through propagation (how it flows) to a sink (where the dangerous operation occurs), including any mitigations checked for and the potential impact if exploited. Every link is independently verifiable against the cited location.
Proof chain
5 steps from untrusted source to potential impact. Each step is independently verifiable against the cited location.
SourceExternal Content - Where
npm:bcryptjs@2.4.3- Observed
Dependency npm:bcryptjs@2.4.3 is within Damerau-Levenshtein distance 2 of bcrypt (threshold 2).
- Why untrusted
- Dependency names are external content resolved from public package registries. A near-miss to a popular canonical name is a supply-chain anomaly under ISO 27001 A.5.21 — the package manager installs whichever spelling is declared, with no built-in guard against lexically similar substitutions.
PropagationDirect Pass - At
package.json/dependencies/bcryptjs- Observed
The manifest entry at /dependencies/bcryptjs directs the package manager to resolve and install bcryptjs@2.4.3. Resolution is purely string-matched against the registry — a typosquatted name installs whatever code the squatter published.
SinkCommand Execution - Where
npm:bcryptjs@2.4.3- Observed
Malicious package `bcryptjs` executes attacker code in the build environment or at import time. Attack classifier: levenshtein-near. Target shadowed: `bcrypt`.
MitigationInput ValidationAbsent - Where
package.json/dependencies/bcryptjs- Detail
- Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
ImpactRemote Code Execution - Scope
- server-host
- Exploitability
- Trivial
- Scenario
- A developer installs `bcryptjs` by typo, copy-paste, or autocomplete. The package's postinstall hook runs during installation with the developer's or CI runner's credentials, or the payload executes on first import when the MCP server starts. An MCP server compromised this way delegates full tool authority to attacker code on every downstream agent interaction.
- +0.1input-validation absentNo input-validation found — Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
- +0.1target_distance_under_thresholdDamerau-Levenshtein distance 2 between `bcryptjs` and `bcrypt` is within the target's declared ceiling of 2. Combined with the Jaro-Winkler agreement check, this is the distance-only classifier — the most common class.
- +0.06algorithm_agreement_highJaro-Winkler similarity 0.950 ≥ 0.90 — two complementary algorithms (Damerau-Levenshtein + Jaro-Winkler) agree on the similarity claim. Agreement across algorithms is the filter against single-algorithm noise.
- -0.04legitimate_fork_allowlist_consultedThe candidate was not in legitimate-forks.ts at scan time. The rule records this explicitly so the finding can be dismissed by adding to the allowlist, with audit trail, if the reviewer confirms the dependency is a sanctioned variant.
- -0.02charter_confidence_capD3 charter caps confidence at 0.9 — similarity is fuzzy and a candidate within Damerau-Levenshtein distance of a popular target can still be a legitimate namespace fork or internal alias the allowlist has not yet captured. The 0.10 gap signals "strong static evidence, reviewer should corroborate against the public registry before removal".
ISO-27001-A.5.21ISO/IEC 27001:2022 Annex A Control 5.21 — ICT Supply Chain Security
A.5.21 requires processes to verify third-party suppliers and the components they deliver. A lexically near-miss dependency name is a supply-chain anomaly that the control requires be detected and reviewed before the component is accepted.
- 1
check-dependencyOpen the manifest and confirm the dependency `bcryptjs@2.4.3` is present. The scanner's similarity pipeline matched this name against the curated target `bcrypt` via the levenshtein-near classifier. If this dependency is an intentional internal fork or re-export, add it to `legitimate-forks.ts` so the finding will no longer fire.
Target:
npm:bcryptjs@2.4.3Expect: Dependency npm:bcryptjs@2.4.3 is declared; it is NOT in the legitimate-fork allowlist at scan time.
- 2
check-dependencyRecompute the Damerau-Levenshtein distance and Jaro-Winkler similarity between `bcryptjs` and `bcrypt` using the same primitives as the scanner. Concretely, the rule expects Damerau-Levenshtein ≤ 2 and Jaro-Winkler ≥ 0.80 (except for advisory-registry matches which skip the floor). Observed values: distance 2, Jaro-Winkler 0.950.
Target:
npm:bcryptjs@2.4.3Expect: Damerau-Levenshtein distance between "bcryptjs" and "bcrypt" is 2. Jaro-Winkler is 0.950. The numbers agree with what the rule recorded.
- 3
check-configOpen the package manifest at this RFC 6901 pointer and read the line. Confirm the package name recorded in the manifest is literally `bcryptjs` (not a spelling the build tool fuzzed to) and that no post-resolution rewrite turns this entry into the legitimate `bcrypt`.
Target:
package.json/dependencies/bcryptjsExpect: The manifest entry at package.json/dependencies/bcryptjs resolves to bcryptjs@2.4.3 — the exact name the scanner flagged.
- 4
compare-baselineOpen the npm page for `bcryptjs` and compare against the legitimate `bcrypt`. Check: publisher identity, publish date, weekly download count, repository link, postinstall script presence. A typosquat typically presents as: recently published, low download count, no repository link, optionally carrying a postinstall hook that executes code at install time.
Target:
npm:bcryptjs@2.4.3Expect: Either the candidate is a legitimate publisher-authored alternative (in which case add to `legitimate-forks.ts`) or its metadata confirms the typosquat hypothesis (recent, unknown publisher, low downloads, suspicious scripts).
Proof chain
5 steps from untrusted source to potential impact. Each step is independently verifiable against the cited location.
SourceExternal Content - Where
npm:cbor@9.0.2- Observed
Dependency npm:cbor@9.0.2 is within Damerau-Levenshtein distance 2 of cors (threshold 2).
- Why untrusted
- Dependency names are external content resolved from public package registries. A near-miss to a popular canonical name is a supply-chain anomaly under ISO 27001 A.5.21 — the package manager installs whichever spelling is declared, with no built-in guard against lexically similar substitutions.
PropagationDirect Pass - At
package.json/dependencies/cbor- Observed
The manifest entry at /dependencies/cbor directs the package manager to resolve and install cbor@9.0.2. Resolution is purely string-matched against the registry — a typosquatted name installs whatever code the squatter published.
SinkCommand Execution - Where
npm:cbor@9.0.2- Observed
Malicious package `cbor` executes attacker code in the build environment or at import time. Attack classifier: levenshtein-near. Target shadowed: `cors`.
MitigationInput ValidationAbsent - Where
package.json/dependencies/cbor- Detail
- Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
ImpactRemote Code Execution - Scope
- server-host
- Exploitability
- Trivial
- Scenario
- A developer installs `cbor` by typo, copy-paste, or autocomplete. The package's postinstall hook runs during installation with the developer's or CI runner's credentials, or the payload executes on first import when the MCP server starts. An MCP server compromised this way delegates full tool authority to attacker code on every downstream agent interaction.
- +0.1input-validation absentNo input-validation found — Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
- +0.1target_distance_under_thresholdDamerau-Levenshtein distance 2 between `cbor` and `cors` is within the target's declared ceiling of 2. Combined with the Jaro-Winkler agreement check, this is the distance-only classifier — the most common class.
- +0.02algorithm_agreement_moderateJaro-Winkler similarity 0.850 clears the agreement floor (0.80) but is below the high-agreement band — the finding stands but reviewer confirmation is advised.
- -0.04legitimate_fork_allowlist_consultedThe candidate was not in legitimate-forks.ts at scan time. The rule records this explicitly so the finding can be dismissed by adding to the allowlist, with audit trail, if the reviewer confirms the dependency is a sanctioned variant.
ISO-27001-A.5.21ISO/IEC 27001:2022 Annex A Control 5.21 — ICT Supply Chain Security
A.5.21 requires processes to verify third-party suppliers and the components they deliver. A lexically near-miss dependency name is a supply-chain anomaly that the control requires be detected and reviewed before the component is accepted.
- 1
check-dependencyOpen the manifest and confirm the dependency `cbor@9.0.2` is present. The scanner's similarity pipeline matched this name against the curated target `cors` via the levenshtein-near classifier. If this dependency is an intentional internal fork or re-export, add it to `legitimate-forks.ts` so the finding will no longer fire.
Target:
npm:cbor@9.0.2Expect: Dependency npm:cbor@9.0.2 is declared; it is NOT in the legitimate-fork allowlist at scan time.
- 2
check-dependencyRecompute the Damerau-Levenshtein distance and Jaro-Winkler similarity between `cbor` and `cors` using the same primitives as the scanner. Concretely, the rule expects Damerau-Levenshtein ≤ 2 and Jaro-Winkler ≥ 0.80 (except for advisory-registry matches which skip the floor). Observed values: distance 2, Jaro-Winkler 0.850.
Target:
npm:cbor@9.0.2Expect: Damerau-Levenshtein distance between "cbor" and "cors" is 2. Jaro-Winkler is 0.850. The numbers agree with what the rule recorded.
- 3
check-configOpen the package manifest at this RFC 6901 pointer and read the line. Confirm the package name recorded in the manifest is literally `cbor` (not a spelling the build tool fuzzed to) and that no post-resolution rewrite turns this entry into the legitimate `cors`.
Target:
package.json/dependencies/cborExpect: The manifest entry at package.json/dependencies/cbor resolves to cbor@9.0.2 — the exact name the scanner flagged.
- 4
compare-baselineOpen the npm page for `cbor` and compare against the legitimate `cors`. Check: publisher identity, publish date, weekly download count, repository link, postinstall script presence. A typosquat typically presents as: recently published, low download count, no repository link, optionally carrying a postinstall hook that executes code at install time.
Target:
npm:cbor@9.0.2Expect: Either the candidate is a legitimate publisher-authored alternative (in which case add to `legitimate-forks.ts`) or its metadata confirms the typosquat hypothesis (recent, unknown publisher, low downloads, suspicious scripts).
Proof chain
5 steps from untrusted source to potential impact. Each step is independently verifiable against the cited location.
SourceExternal Content - Where
pypi:flat@6.0.1- Observed
Dependency pypi:flat@6.0.1 is within Damerau-Levenshtein distance 2 of flask (threshold 2).
- Why untrusted
- Dependency names are external content resolved from public package registries. A near-miss to a popular canonical name is a supply-chain anomaly under ISO 27001 A.5.21 — the package manager installs whichever spelling is declared, with no built-in guard against lexically similar substitutions.
PropagationDirect Pass - At
pyproject.toml/project/dependencies/flat- Observed
The manifest entry at /project/dependencies/flat directs the package manager to resolve and install flat@6.0.1. Resolution is purely string-matched against the registry — a typosquatted name installs whatever code the squatter published.
SinkCommand Execution - Where
pypi:flat@6.0.1- Observed
Malicious package `flat` executes attacker code in the build environment or at import time. Attack classifier: levenshtein-near. Target shadowed: `flask`.
MitigationInput ValidationAbsent - Where
pyproject.toml/project/dependencies/flat- Detail
- Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
ImpactRemote Code Execution - Scope
- server-host
- Exploitability
- Trivial
- Scenario
- A developer installs `flat` by typo, copy-paste, or autocomplete. The package's postinstall hook runs during installation with the developer's or CI runner's credentials, or the payload executes on first import when the MCP server starts. An MCP server compromised this way delegates full tool authority to attacker code on every downstream agent interaction.
- +0.1input-validation absentNo input-validation found — Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
- +0.1target_distance_under_thresholdDamerau-Levenshtein distance 2 between `flat` and `flask` is within the target's declared ceiling of 2. Combined with the Jaro-Winkler agreement check, this is the distance-only classifier — the most common class.
- +0.02algorithm_agreement_moderateJaro-Winkler similarity 0.848 clears the agreement floor (0.80) but is below the high-agreement band — the finding stands but reviewer confirmation is advised.
- -0.04legitimate_fork_allowlist_consultedThe candidate was not in legitimate-forks.ts at scan time. The rule records this explicitly so the finding can be dismissed by adding to the allowlist, with audit trail, if the reviewer confirms the dependency is a sanctioned variant.
ISO-27001-A.5.21ISO/IEC 27001:2022 Annex A Control 5.21 — ICT Supply Chain Security
A.5.21 requires processes to verify third-party suppliers and the components they deliver. A lexically near-miss dependency name is a supply-chain anomaly that the control requires be detected and reviewed before the component is accepted.
- 1
check-dependencyOpen the manifest and confirm the dependency `flat@6.0.1` is present. The scanner's similarity pipeline matched this name against the curated target `flask` via the levenshtein-near classifier. If this dependency is an intentional internal fork or re-export, add it to `legitimate-forks.ts` so the finding will no longer fire.
Target:
pypi:flat@6.0.1Expect: Dependency pypi:flat@6.0.1 is declared; it is NOT in the legitimate-fork allowlist at scan time.
- 2
check-dependencyRecompute the Damerau-Levenshtein distance and Jaro-Winkler similarity between `flat` and `flask` using the same primitives as the scanner. Concretely, the rule expects Damerau-Levenshtein ≤ 2 and Jaro-Winkler ≥ 0.80 (except for advisory-registry matches which skip the floor). Observed values: distance 2, Jaro-Winkler 0.848.
Target:
pypi:flat@6.0.1Expect: Damerau-Levenshtein distance between "flat" and "flask" is 2. Jaro-Winkler is 0.848. The numbers agree with what the rule recorded.
- 3
check-configOpen the package manifest at this RFC 6901 pointer and read the line. Confirm the package name recorded in the manifest is literally `flat` (not a spelling the build tool fuzzed to) and that no post-resolution rewrite turns this entry into the legitimate `flask`.
Target:
pyproject.toml/project/dependencies/flatExpect: The manifest entry at pyproject.toml/project/dependencies/flat resolves to flat@6.0.1 — the exact name the scanner flagged.
- 4
compare-baselineOpen the PyPI page for `flat` and compare against the legitimate `flask`. Check: publisher identity, publish date, weekly download count, repository link, postinstall script presence. A typosquat typically presents as: recently published, low download count, no repository link, optionally carrying a postinstall hook that executes code at install time.
Target:
pypi:flat@6.0.1Expect: Either the candidate is a legitimate publisher-authored alternative (in which case add to `legitimate-forks.ts`) or its metadata confirms the typosquat hypothesis (recent, unknown publisher, low downloads, suspicious scripts).
Proof chain
5 steps from untrusted source to potential impact. Each step is independently verifiable against the cited location.
SourceExternal Content - Where
npm:core-js@3.37.1- Observed
Dependency npm:core-js@3.37.1 is within Damerau-Levenshtein distance 2 of cors (threshold 2).
- Why untrusted
- Dependency names are external content resolved from public package registries. A near-miss to a popular canonical name is a supply-chain anomaly under ISO 27001 A.5.21 — the package manager installs whichever spelling is declared, with no built-in guard against lexically similar substitutions.
PropagationDirect Pass - At
package.json/dependencies/core-js- Observed
The manifest entry at /dependencies/core-js directs the package manager to resolve and install core-js@3.37.1. Resolution is purely string-matched against the registry — a typosquatted name installs whatever code the squatter published.
SinkCommand Execution - Where
npm:core-js@3.37.1- Observed
Malicious package `core-js` executes attacker code in the build environment or at import time. Attack classifier: levenshtein-near. Target shadowed: `cors`.
MitigationInput ValidationAbsent - Where
package.json/dependencies/core-js- Detail
- Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
ImpactRemote Code Execution - Scope
- server-host
- Exploitability
- Trivial
- Scenario
- A developer installs `core-js` by typo, copy-paste, or autocomplete. The package's postinstall hook runs during installation with the developer's or CI runner's credentials, or the payload executes on first import when the MCP server starts. An MCP server compromised this way delegates full tool authority to attacker code on every downstream agent interaction.
- +0.1input-validation absentNo input-validation found — Lockfiles pin versions but do not pin the spelling of the dependency name. The static analyser cannot confirm whether a typosquat-aware package firewall (Socket.dev, Snyk Advisor) is in the CI chain; the auditor must verify.
- +0.1target_distance_under_thresholdDamerau-Levenshtein distance 2 between `core-js` and `cors` is within the target's declared ceiling of 2. Combined with the Jaro-Winkler agreement check, this is the distance-only classifier — the most common class.
- +0.02algorithm_agreement_moderateJaro-Winkler similarity 0.808 clears the agreement floor (0.80) but is below the high-agreement band — the finding stands but reviewer confirmation is advised.
- -0.04legitimate_fork_allowlist_consultedThe candidate was not in legitimate-forks.ts at scan time. The rule records this explicitly so the finding can be dismissed by adding to the allowlist, with audit trail, if the reviewer confirms the dependency is a sanctioned variant.
ISO-27001-A.5.21ISO/IEC 27001:2022 Annex A Control 5.21 — ICT Supply Chain Security
A.5.21 requires processes to verify third-party suppliers and the components they deliver. A lexically near-miss dependency name is a supply-chain anomaly that the control requires be detected and reviewed before the component is accepted.
- 1
check-dependencyOpen the manifest and confirm the dependency `core-js@3.37.1` is present. The scanner's similarity pipeline matched this name against the curated target `cors` via the levenshtein-near classifier. If this dependency is an intentional internal fork or re-export, add it to `legitimate-forks.ts` so the finding will no longer fire.
Target:
npm:core-js@3.37.1Expect: Dependency npm:core-js@3.37.1 is declared; it is NOT in the legitimate-fork allowlist at scan time.
- 2
check-dependencyRecompute the Damerau-Levenshtein distance and Jaro-Winkler similarity between `core-js` and `cors` using the same primitives as the scanner. Concretely, the rule expects Damerau-Levenshtein ≤ 2 and Jaro-Winkler ≥ 0.80 (except for advisory-registry matches which skip the floor). Observed values: distance 2, Jaro-Winkler 0.808.
Target:
npm:core-js@3.37.1Expect: Damerau-Levenshtein distance between "core-js" and "cors" is 2. Jaro-Winkler is 0.808. The numbers agree with what the rule recorded.
- 3
check-configOpen the package manifest at this RFC 6901 pointer and read the line. Confirm the package name recorded in the manifest is literally `core-js` (not a spelling the build tool fuzzed to) and that no post-resolution rewrite turns this entry into the legitimate `cors`.
Target:
package.json/dependencies/core-jsExpect: The manifest entry at package.json/dependencies/core-js resolves to core-js@3.37.1 — the exact name the scanner flagged.
- 4
compare-baselineOpen the npm page for `core-js` and compare against the legitimate `cors`. Check: publisher identity, publish date, weekly download count, repository link, postinstall script presence. A typosquat typically presents as: recently published, low download count, no repository link, optionally carrying a postinstall hook that executes code at install time.
Target:
npm:core-js@3.37.1Expect: Either the candidate is a legitimate publisher-authored alternative (in which case add to `legitimate-forks.ts`) or its metadata confirms the typosquat hypothesis (recent, unknown publisher, low downloads, suspicious scripts).