Skip to content

1.0.0 readiness: version-resolution, parser, security, and release fixes - #28

Merged
math280h merged 6 commits into
mainfrom
fix/1.0-readiness
Jul 20, 2026
Merged

math280h merged 6 commits into
mainfrom
fix/1.0-readiness

Conversation

@math280h

Copy link
Copy Markdown
Owner

Prepares the project for a 1.0.0 release. Found during a multi-dimension pre-1.0 audit (parsers, crash-safety, security/fail-closed, check logic, release hygiene); every fix below was verified live against the built binary.

Blockers

check_package rejected ranged/partial versions as "hallucinated." The primary agent tool forwarded the raw version to an exact-match lookup, so react "18", "^4.17.0", serde "1", requests ">=2" were denied as nonexistent, on all three registries — a false block on the most common workflow ("add react 18"). Versions are now resolved per ecosystem to the best matching published version; only a concrete exact version that is genuinely absent is still flagged. Verified: check_package(react,"18") now resolves to react@18.3.1 and allows.

MCP server advertised the wrong identity. get_info did not set server_info, so every MCP host saw rmcp/<rmcp version>. Now advertises safe-pkgs/1.0.0. Verified over stdio.

Version bump. Workspace version 0.2.0 → 1.0.0 (single source; propagates to all crates).

Correctness (parsers)

  • Local/git/URL deps no longer false-flagged. npm (file:, git, tarball URL, workspace:/link:) and PyPI (Poetry path/git/url, PEP 508 name @ url) dependencies are skipped rather than looked up on the public registry and reported as nonexistent — matching cargo's existing behavior. npm aliases (npm:real-pkg@1.2.3) now audit the real target. Verified: a package.json with file:/github: deps audits only the real packages.
  • PyPI multi-specifier lines no longer dropped. torch>1.9,<2.0 and numpy!=1.24.0,>=1.20 were mis-split by operator list-order and silently skipped; now split on the leftmost operator. Verified: all three deps parsed.

Security

  • Denylist / allowlist / dependency-confusion now compare canonical names. An equivalent spelling (case, and -/_/. per PyPI/crates.io) could bypass a rule. Verified: denylisting my-internal now blocks my_internal on PyPI.
  • Bearer token no longer sent over cleartext http (except loopback), matching the audit and remote-config guards. Token is still sent only to the metadata host.

Release hygiene

  • Package metadata: license = "MIT", rust-version = "1.85", repository, description, keywords, categories, publish = false (binary distribution).
  • CI: clippy and test now run with --workspace (previously only the root package was gated).
  • README: corrected the rmcp badge and the metadata note. Added CHANGELOG.md; ignored stray .tmp//.cache//WORKFLOW.md.

Known limitation (documented, not fixed here)

A lockfile pinning the same package at multiple versions (e.g. windows-sys 0.48 and 0.52 in a Cargo.lock) is audited at a single version; the other pinned version is not evaluated. This is an architectural change to the dependency model and is deferred to a focused follow-up (noted in CHANGELOG). All other audited findings were either fixed above or confirmed non-issues (no reachable panics, fail-closed audit trail, correct risk aggregation).

Testing

  • cargo fmt / clippy --workspace --all-targets -D warnings / cargo test --workspace — all green (215 tests; new regression tests for version resolution, name canonicalization, npm non-registry filtering, and PyPI multi-specifier parsing).
  • Live re-verification of every fix above against the installed safe-pkgs v1.0.0 binary and the MCP server.

math280h added 6 commits July 20, 2026 17:01
Set the workspace version to 1.0.0 and add distribution metadata: license
(MIT), rust-version (1.85, matching edition 2024), repository, description,
readme, keywords, categories, and publish = false (the project ships as a
binary via release artifacts and `cargo install --path .`, not to crates.io).
Add the semver dependency to the core crate for version-requirement resolution.
check_package forwarded the raw requested version to an exact-match lookup, so
a range or partial requirement (`react` "18", "^4.17.0", `serde` "1",
`requests` ">=2") was denied as a nonexistent "hallucinated" version — a false
block on the primary agent workflow, on all three registries. Resolve the
requirement per ecosystem to the best matching published version (semver for
npm/cargo, PEP 440 specifiers for PyPI); only a concrete exact version that is
genuinely absent is still flagged.

Also compare canonical names in denylist/allowlist/dependency-confusion matching
so an equivalent spelling (case, and `-`/`_`/`.` per PyPI/crates.io rules) cannot
bypass a rule (e.g. denylisting `evil-pkg` now also blocks `evil_pkg`).
npm and PyPI manifests emitted local/git/URL dependencies as if they were
registry packages, producing false "does not exist" findings for a project's
own path/git/private deps (cargo already filtered these). Skip `file:`, git,
tarball-URL, and `workspace:`/`link:` (npm) and `path`/`git`/`url` (Poetry) and
PEP 508 `name @ url` dependencies; resolve npm aliases (`npm:real-pkg@1.2.3`) to
the real target.

PyPI requirement lines were split by operator list-order, so multi-specifier
lines like `torch>1.9,<2.0` and `numpy!=1.24.0,>=1.20` were mis-split and
silently dropped. Split on the leftmost operator instead.
get_info did not set server_info, so rmcp reported its own crate name and
version (`rmcp`/<rmcp version>) to every MCP host. Advertise the crate's own
name and version instead.
The npm and PyPI clients attached a private-registry bearer token
unconditionally, while the base URL is env-overridable to http://, so a token
could be sent in cleartext to a remote mirror. Gate the token on HTTPS (or an
http loopback host for local testing), mirroring the audit and remote-config
guards. The token is still sent only to the metadata host, never to the
downloads or popularity hosts.
Fix the README rmcp badge (1.x) and the decision-payload metadata note (advisories
are evidence, not metadata). Run clippy and test with --workspace so every crate
is gated in CI. Add a 1.0.0 CHANGELOG and ignore stray .tmp/.cache/WORKFLOW.md.
Copilot AI review requested due to automatic review settings July 20, 2026 21:02
@github-actions

Copy link
Copy Markdown

Coverage

Coverage Report

Metric Value
Total lines 6954
Covered lines 5986
Missed lines 968
Coverage 86.08%
Tests passed 215
Tests failed 0
Tests ignored 0
Coverage command exit 0
Per-file coverage (top 40 by missed lines)
File Stmts Miss Cover Missing
/home/runner/work/safe-pkgs/safe-pkgs/src/custom_rules.rs 194 119 38.66% 41, 70, 71, 80, 81, 82, 83, 96, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/service.rs 441 113 74.38% 32, 33, 34, 38, 39, 40, 62, 63, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/config/custom_rules.rs 152 67 55.92% 134, 161, 164, 170, 173, 192, 193, 195, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/core/src/lib.rs 480 56 88.33% 82, 83, 84, 100, 101, 102, 118, 119, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/staleness/src/lib.rs 210 55 73.81% 63, 64, 65, 66, 67, 68, 69, 70, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/config/mod.rs 334 54 83.83% 311, 312, 313, 323, 324, 325, 326, 327, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/policy_snapshot.rs 215 48 77.67% 127, 153, 154, 155, 156, 157, 158, 159, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/main.rs 100 48 52.00% 44, 114, 117, 118, 119, 120, 121, 122, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/mcp/server.rs 126 32 74.60% 129, 130, 131, 153, 154, 156, 157, 158, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/cargo/src/lockfile.rs 423 32 92.43% 28, 29, 30, 45, 46, 47, 64, 73, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/checks.rs 469 31 93.39% 189, 237, 238, 239, 240, 241, 242, 243, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/pypi/src/lockfile.rs 394 24 93.91% 26, 27, 28, 43, 44, 45, 62, 63, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/http/src/lib.rs 206 24 88.35% 46, 47, 52, 53, 65, 66, 67, 68, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/cache.rs 127 22 82.68% 25, 26, 27, 28, 29, 30, 31, 32, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/npm/src/lockfile.rs 457 21 95.40% 27, 28, 29, 44, 45, 46, 66, 94, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/audit_log.rs 143 19 86.71% 95, 143, 145, 179, 180, 181, 183, 185, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/bin/safe-pkgs-mcp.rs 37 16 56.76% 12, 13, 14, 15, 16, 18, 21, 22, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/npm/src/registry.rs 373 14 96.25% 93, 121, 122, 123, 124, 141, 142, 143, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/typosquat/src/lib.rs 119 13 89.08% 68, 79, 82, 83, 91, 120, 162, 163, ...
/home/runner/work/safe-pkgs/safe-pkgs/src/registries/mod.rs 149 9 93.96% 49, 50, 51, 124, 144, 153, 154, 155, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/pypi/src/registry.rs 277 9 96.75% 75, 76, 77, 82, 83, 84, 241, 243, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/osv/src/lib.rs 147 9 93.88% 12, 13, 14, 15, 16, 17, 18, 19, ...
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/cargo/src/registry.rs 299 5 98.33% 57, 58, 59, 230, 232
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/existence/src/lib.rs 48 3 93.75% 36, 37, 38
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/advisory/src/lib.rs 112 3 97.32% 67, 128, 141
/home/runner/work/safe-pkgs/safe-pkgs/src/support_map.rs 142 1 99.30% 58
/home/runner/work/safe-pkgs/safe-pkgs/src/metrics.rs 92 0 100.00% -
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/pypi/src/lib.rs 14 0 100.00% -
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/npm/src/lib.rs 14 0 100.00% -
/home/runner/work/safe-pkgs/safe-pkgs/crates/registry/cargo/src/lib.rs 14 0 100.00% -
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/version-age/src/lib.rs 66 0 100.00% -
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/popularity/src/lib.rs 81 0 100.00% -
/home/runner/work/safe-pkgs/safe-pkgs/crates/checks/install-script/src/lib.rs 55 0 100.00% -

@math280h
math280h merged commit 4f8262f into main Jul 20, 2026
6 checks passed
@math280h
math280h deleted the fix/1.0-readiness branch July 20, 2026 21:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prepares safe-pkgs for a 1.0.0 release by fixing pre-1.0 audit blockers across version resolution, dependency parsing, security fail-closed behavior, and release/CI metadata—improving correctness for common real-world dependency specs across npm, PyPI, and crates.io.

Changes:

  • Add ecosystem-aware version resolution (resolve_version_for) so ranged/partial versions don’t get incorrectly treated as nonexistent.
  • Harden policy matching via canonicalized package-name equivalence and skip non-registry dependency sources in npm/PyPI parsers.
  • Improve release hygiene (1.0.0 bump, metadata, CI --workspace, changelog) and MCP server identity reporting.

Reviewed changes

Copilot reviewed 14 out of 16 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/mcp/server.rs Advertises safe-pkgs identity/version via MCP server info.
src/config/mod.rs Canonicalized matching for dependency-confusion config rules.
src/checks.rs Uses ecosystem-aware canonicalization and version resolution in policy checks.
README.md Updates rmcp badge/version note and metadata note.
crates/registry/pypi/src/registry.rs Applies transport-safe token attachment for PyPI registry auth.
crates/registry/pypi/src/lockfile.rs Skips non-registry Poetry/PEP508 URL deps; fixes multi-specifier parsing.
crates/registry/npm/src/registry.rs Applies transport-safe token attachment for npm registry auth.
crates/registry/npm/src/lockfile.rs Skips non-registry npm specs; supports npm alias targets.
crates/http/src/lib.rs Adds helper to prevent sending bearer tokens over non-secure transports.
crates/core/src/lib.rs Adds canonical name equivalence + ecosystem-aware version resolution logic + tests.
crates/core/Cargo.toml Adds semver dependency required by new version-resolution code.
CHANGELOG.md Adds 1.0.0 changelog including known limitation documentation.
Cargo.toml Bumps workspace to 1.0.0; adds crate metadata and workspace rust-version/license/repository.
Cargo.lock Updates lockfile for 1.0.0 versions and new deps.
.gitignore Ignores .cache, .tmp, and WORKFLOW.md.
.github/workflows/ci.yml Runs clippy/tests for the entire workspace.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +337 to +340
// Bare "owner/repo" GitHub shorthand: contains a slash, no protocol, and is not a
// scoped "@scope/name" registry package.
!spec.starts_with('@') && spec.contains('/') && !spec.contains(':')
}
Comment thread crates/http/src/lib.rs
Comment on lines +63 to +77
pub fn token_for_transport(base_url: &str, token: Option<String>) -> Option<String> {
let token = token?;
if let Ok(url) = reqwest::Url::parse(base_url.trim()) {
let is_https = url.scheme().eq_ignore_ascii_case("https");
let is_http_loopback = url.scheme().eq_ignore_ascii_case("http")
&& url.host_str().is_some_and(is_loopback_host);
if is_https || is_http_loopback {
return Some(token);
}
}
tracing::warn!(
"skipping registry bearer token: base URL is not HTTPS (would send credentials in cleartext)"
);
None
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants