Skip to content

Commit 327df37

Browse files
committed
fix(security): resolve RUSTSEC-2026-0204 and consolidate safe dep bumps
Upgrade crossbeam-epoch to 0.9.20 and spin to 0.9.9 so cargo audit is clean on main. Apply compatible Dependabot lockfile bumps in one change and fix the clippy lint that was failing Lint on those PRs. Also ship MCP admin_token HTTP enforcement, query-arg forwarding, and protocol-mcp project templates.
1 parent a544ccf commit 327df37

12 files changed

Lines changed: 731 additions & 68 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- **MCP HTTP admin token enforcement**: when `McpConfig::admin_token` is set, the sidecar rejects unauthenticated JSON-RPC with `401` (`Authorization: Bearer`, `X-MCP-Token`, or `?token=`).
13+
- **MCP query-parameter tool args**: remaining non-path arguments on safe methods are forwarded as a query string (proxy + in-process URI).
14+
- **`cargo rustapi mcp generate --admin-token`** (env: `RUSTAPI_MCP_TOKEN`).
15+
- **`cargo rustapi new`**: `protocol-mcp` feature option; `ai-api` preset includes it; minimal template wires `McpServer` + `RUSTAPI_MCP_TOKEN` when selected.
1216
- **`docs/GOLDEN_PATH.md`** — canonical handler → OpenAPI → probes → MCP/deploy walkthrough; linked from README, docs hub, cookbook SUMMARY, Getting Started.
1317
- **`golden_path` example** uses route macros + tags so `/docs/openapi.json` includes `/api/v1/ping` (not only `.route()` wiring).
1418

1519
### Changed
1620

21+
- **Dependabot consolidation**: lockfile bumps for `open` 5.4.0, `toml` 1.1.3, `uuid` 1.23.5, `http-body-util` 0.1.4, `bytes` 1.12.1, `simd-json` 0.17.3, `regex` 1.13.0, `rustls` 0.23.42, `redis` 1.4.0 (supersedes open Dependabot PRs #228#236; skips major `rand` 0.8→0.10).
1722
- **Production Readiness v0.2** ([#200](https://github.com/Tuntii/RustAPI/issues/200)): coverage CI publishes HTML + Cobertura artifacts with job summary; release-drafter publishes drafts on `v*` tags; README links [Production Checklist](docs/PRODUCTION_CHECKLIST.md) in the header.
1823
- README Quick Start: golden path first; document OpenAPI at `/docs/openapi.json`; prefer macros for OpenAPI registration; `tracing-subscriber` + `production_defaults` in the hello snippet.
1924
- Dependency updates: `actions/checkout@v7`, `actions/cache@v6`, `brotli` 8, `rcgen` 0.14, `tera` 2, `thiserror` 2, OpenTelemetry 0.32 stack, `sqlx` 0.9, `tracing-opentelemetry` 0.33.
@@ -24,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2429

2530
### Fixed
2631

32+
- **Security Audit**: upgrade transitive `crossbeam-epoch` 0.9.18 → 0.9.20 (`RUSTSEC-2026-0204`); un-yank `spin` 0.9.8 → 0.9.9.
33+
- **Clippy** (`-D warnings`): remove redundant borrow in `rustapi-core` validation message formatting (was failing Lint on Dependabot PRs).
2734
- **`rcgen` 0.14 HTTP/3 dev certs**: `generate_self_signed_cert` uses `CertifiedKey { cert, signing_key }` so `--all-features` / `http3-dev` builds pass CI again.
2835
- **SQLx 0.9 jobs + CRUD generator**: Postgres job backend uses `AssertSqlSafe` and `Json` payloads; `cargo rustapi generate crud` emits SQLx 0.9-compatible SQLite handlers.
2936
- **Security Audit** (`cargo audit`) passes on the current lockfile (no high-severity `quick-xml` advisories).

Cargo.lock

Lines changed: 35 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cargo-rustapi/src/commands/mcp.rs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ pub struct McpGenerateArgs {
5959
/// MCP over standard input/output.
6060
#[arg(long)]
6161
pub stdio: bool,
62+
63+
/// Require this token on the HTTP MCP transport
64+
/// (`Authorization: Bearer`, `X-MCP-Token`, or `?token=`).
65+
///
66+
/// Prefer setting this for any network-exposed MCP endpoint.
67+
/// If omitted, falls back to the `RUSTAPI_MCP_TOKEN` environment variable.
68+
#[arg(long)]
69+
pub admin_token: Option<String>,
6270
}
6371

6472
/// Execute `rustapi mcp generate`
@@ -115,13 +123,25 @@ pub async fn mcp_generate(args: McpGenerateArgs) -> Result<()> {
115123
config = config.allow_path_prefix(prefix.clone());
116124
}
117125

126+
let admin_token = args
127+
.admin_token
128+
.clone()
129+
.or_else(|| std::env::var("RUSTAPI_MCP_TOKEN").ok())
130+
.filter(|t| !t.is_empty());
131+
if let Some(token) = &admin_token {
132+
config = config.admin_token(token.clone());
133+
}
134+
118135
let mut mcp = McpServer::from_spec(config, &openapi);
119136
mcp = mcp.with_http_base(target.clone());
120137

121138
let addr = format!("127.0.0.1:{}", args.port); // safer default for local tool
122139

123140
println!(" ✓ Spec loaded");
124141
println!(" → Proxying tool calls to: {}", target);
142+
if admin_token.is_some() {
143+
println!(" → Admin token required on HTTP transport");
144+
}
125145

126146
if args.stdio {
127147
println!("🧠 MCP stdio transport active. Waiting for JSON-RPC on stdin...");
@@ -132,15 +152,20 @@ pub async fn mcp_generate(args: McpGenerateArgs) -> Result<()> {
132152
println!(" → MCP server listening on: http://{}", addr);
133153
println!();
134154
println!("Useful test commands:");
155+
let auth_header = if admin_token.is_some() {
156+
" -H 'Authorization: Bearer $RUSTAPI_MCP_TOKEN'"
157+
} else {
158+
""
159+
};
135160
println!(
136-
" curl -X POST http://127.0.0.1:{} -H 'content-type: application/json' \\",
137-
args.port
161+
" curl -X POST http://127.0.0.1:{} -H 'content-type: application/json'{} \\",
162+
args.port, auth_header
138163
);
139164
println!(" -d '{{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\"}}'");
140165
println!();
141166
println!(
142-
" curl -X POST http://127.0.0.1:{} -H 'content-type: application/json' \\",
143-
args.port
167+
" curl -X POST http://127.0.0.1:{} -H 'content-type: application/json'{} \\",
168+
args.port, auth_header
144169
);
145170
println!(" -d '{{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\"}}'");
146171
println!();

crates/cargo-rustapi/src/commands/new.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,16 +141,17 @@ pub async fn new_project(mut args: NewArgs) -> Result<()> {
141141
"protocol-ws",
142142
"protocol-view",
143143
"protocol-grpc",
144+
"protocol-mcp",
144145
];
145146
let preset_features = preset
146147
.map(ProjectPreset::recommended_features)
147148
.unwrap_or_default();
148149
let defaults = match template {
149150
ProjectTemplate::Full => vec![
150-
true, true, true, true, false, false, false, false, false, false, false,
151+
true, true, true, true, false, false, false, false, false, false, false, false,
151152
],
152153
ProjectTemplate::Web => vec![
153-
false, false, false, false, false, false, false, false, false, true, false,
154+
false, false, false, false, false, false, false, false, false, true, false, false,
154155
],
155156
_ => vec![false; available.len()],
156157
};
@@ -257,6 +258,17 @@ pub async fn new_project(mut args: NewArgs) -> Result<()> {
257258
style("http://localhost:8080/docs").cyan()
258259
);
259260

261+
if features.iter().any(|f| f == "protocol-mcp") {
262+
println!(
263+
"MCP tools (if enabled in main) at {}",
264+
style("http://localhost:9090").cyan()
265+
);
266+
println!(
267+
" Set {} to require a token on the MCP HTTP transport.",
268+
style("RUSTAPI_MCP_TOKEN").yellow()
269+
);
270+
}
271+
260272
Ok(())
261273
}
262274

0 commit comments

Comments
 (0)