diff --git a/crates/socket-patch-core/src/vendor/maven_repo.rs b/crates/socket-patch-core/src/vendor/maven_repo.rs index 6b2e891c..3863a41f 100644 --- a/crates/socket-patch-core/src/vendor/maven_repo.rs +++ b/crates/socket-patch-core/src/vendor/maven_repo.rs @@ -63,7 +63,6 @@ use serde_json::Value; use sha1::Sha1; use sha2::{Digest as _, Sha256}; -use crate::constants::USER_AGENT; use crate::manifest::schema::{PatchFileInfo, PatchRecord}; use crate::patch::apply::{ApplyResult, PatchSources}; use crate::patch::copy_tree::remove_tree; @@ -95,6 +94,21 @@ const REPO_WIRING_KIND: &str = "maven_pom_repository"; /// (small XML); a multi-MB response is a mirror serving the wrong thing. const MAX_POM_BYTES: usize = 8 * 1024 * 1024; +/// User-Agent for maven2 registry requests. Maven Central blocks/rate-limits +/// user agents containing "socket", so the CLI's own `SocketPatchCLI/x.y.z` +/// UA (`constants.rs`) gets the pom fallback download refused. These requests +/// instead identify exactly as the official Maven CLI — +/// `Apache-Maven/ (Java ; )`, the shape +/// maven-resolver sends — pinned to fixed Maven/JDK/OS versions so the string +/// stays deterministic (no runtime probing). Only maven2 registry traffic +/// uses this; Socket API requests keep the honest UA. +#[cfg(target_os = "macos")] +const MAVEN_USER_AGENT: &str = "Apache-Maven/3.9.11 (Java 17.0.16; Mac OS X 15.5)"; +#[cfg(target_os = "windows")] +const MAVEN_USER_AGENT: &str = "Apache-Maven/3.9.11 (Java 17.0.16; Windows 11 10.0)"; +#[cfg(not(any(target_os = "macos", target_os = "windows")))] +const MAVEN_USER_AGENT: &str = "Apache-Maven/3.9.11 (Java 17.0.16; Linux 6.8.0)"; + /// The maven2 registry base for the (fallback) pom download, overridable with /// `SOCKET_MAVEN_REGISTRY` (the private-mirror / test escape hatch). Default is /// Maven Central's maven2 endpoint. @@ -730,7 +744,7 @@ async fn acquire_upstream_pom( /// Bounded HTTP GET of a pom from the maven2 registry. async fn fetch_pom_bytes(url: &str) -> Result, String> { let client = reqwest::Client::builder() - .user_agent(USER_AGENT) + .user_agent(MAVEN_USER_AGENT) .timeout(Duration::from_secs(60)) .build() .map_err(|e| format!("build http client: {e}"))?; @@ -2304,6 +2318,46 @@ mod tests { ); } + /// Maven Central blocks/rate-limits user agents containing "socket" — + /// the maven2 registry client must identify as the official Maven CLI, + /// never as `SocketPatchCLI/…`. + #[test] + fn maven_user_agent_is_the_maven_cli_shape() { + assert!( + MAVEN_USER_AGENT.starts_with("Apache-Maven/"), + "maven2 registry UA must lead with the Maven CLI product token: {MAVEN_USER_AGENT}" + ); + assert!( + !MAVEN_USER_AGENT.to_ascii_lowercase().contains("socket"), + "a UA containing \"socket\" is blocked by Maven Central: {MAVEN_USER_AGENT}" + ); + } + + /// The Maven-CLI UA must actually go out on the wire: the mock only + /// serves the pom when the request carries `MAVEN_USER_AGENT`, so a + /// regression back to `SocketPatchCLI/…` misses the matcher and fails + /// the fetch. + #[tokio::test] + async fn pom_fetch_sends_the_maven_cli_user_agent() { + use wiremock::matchers::{header, method, path}; + use wiremock::{Mock, MockServer, ResponseTemplate}; + + let pom_route = "/org/apache/commons/commons-text/1.10.0/commons-text-1.10.0.pom"; + let server = MockServer::start().await; + Mock::given(method("GET")) + .and(path(pom_route)) + .and(header("user-agent", MAVEN_USER_AGENT)) + .respond_with(ResponseTemplate::new(200).set_body_bytes(UPSTREAM_POM.to_vec())) + .expect(1) + .mount(&server) + .await; + + let bytes = fetch_pom_bytes(&format!("{}{pom_route}", server.uri())) + .await + .expect("pom fetch under the Maven CLI UA succeeds"); + assert_eq!(bytes, UPSTREAM_POM); + } + /// A FIFO planted as `pom.xml` must fail the revert fast and loudly — /// keeping the uuid dir for a retry — instead of wedging `--revert` /// forever. diff --git a/npm/socket-patch/package-lock.json b/npm/socket-patch/package-lock.json index 3fe0775a..987aca5f 100644 --- a/npm/socket-patch/package-lock.json +++ b/npm/socket-patch/package-lock.json @@ -38,6 +38,212 @@ "@socketsecurity/socket-patch-win32-x64": "4.0.0" } }, + "node_modules/@socketsecurity/socket-patch-android-arm64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-android-arm64/-/socket-patch-android-arm64-4.0.0.tgz", + "integrity": "sha512-reLUyHSWhfYL/Qqu1O31e1GDJnCOvaO4rXmieGffJA3w6kdNfy8mCuZyaDGruahlkkTEXZcJyqFeToQQ91pXQQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@socketsecurity/socket-patch-darwin-arm64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-darwin-arm64/-/socket-patch-darwin-arm64-4.0.0.tgz", + "integrity": "sha512-7g1VHKEKB+CBa4RCVeaD6cjdttYGLpHv6Nmk21CHwA6JRe9Fgpzr2xIk0IBGtbuINbQxzcZio0SnhO3dFDnoYA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@socketsecurity/socket-patch-darwin-x64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-darwin-x64/-/socket-patch-darwin-x64-4.0.0.tgz", + "integrity": "sha512-WFmHwA+BZ1aKRv+dhBnPU2Rvzd6xJ5YujPSn19wIR9vo7jDE1c2jI0CEqAxSTPnyLyGOUpOrq1g6pmDKVLv86A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-arm-gnu": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-arm-gnu/-/socket-patch-linux-arm-gnu-4.0.0.tgz", + "integrity": "sha512-9K78qxdTowbJajPyXk1RLCySoS1UlZTpSWM6t42lNtfEY11hIFkg50XgucYPu0KJ3Ab42fPz0qbYSOgtsXiX3g==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-arm-musl": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-arm-musl/-/socket-patch-linux-arm-musl-4.0.0.tgz", + "integrity": "sha512-GxWDBL7QKQngtCOEQROKHtWcPnZ7m0stSt+TU1WmF4k5jsH7OeBRzdmSnZeTGTZNUsTHqgI/YdaqJnnfoR0enA==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-arm64-gnu": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-arm64-gnu/-/socket-patch-linux-arm64-gnu-4.0.0.tgz", + "integrity": "sha512-z4RD7hynIwe7E6Gr3N2oq7q4InZqVw0vdxXs8ghYqMhjluu0NcC0Vc/bsvDSmURG6DhHufJ+ExjHtrpebmujFw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-arm64-musl": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-arm64-musl/-/socket-patch-linux-arm64-musl-4.0.0.tgz", + "integrity": "sha512-GhqDojBf6ZUXE/JzcWSNnM9H9bt3lTCu0qpUCXzX5NEttpyxx1XLZx/ujFIwMqx2eSTPl7ijnjz4C/6QNw2XGw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-ia32-gnu": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-ia32-gnu/-/socket-patch-linux-ia32-gnu-4.0.0.tgz", + "integrity": "sha512-7HJbJ++YO757LMmLtcSEMt7ITnPGhfpmR8OKJtxNZFfv+tl7wLXH8Nndz98t4xdOngiO/hFJ1qAI35F8DFZ+xw==", + "cpu": [ + "ia32" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-ia32-musl": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-ia32-musl/-/socket-patch-linux-ia32-musl-4.0.0.tgz", + "integrity": "sha512-NTca+HBv1v4ccn+tznnCnzZH/7vZxsJzTnochaHX4D1nECga0LlFkApsP5T3Dlv+5pFy1UWpaadEasMYnAfmYg==", + "cpu": [ + "ia32" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-x64-gnu": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-x64-gnu/-/socket-patch-linux-x64-gnu-4.0.0.tgz", + "integrity": "sha512-+XerR7nK0dtbcjXLXG6upm6HBmeAXW+OQrzXs2WpU61bxUQugWQtXg5iifL0Niakb7v+gSR7iAxBv8pUIeXGMw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-linux-x64-musl": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-linux-x64-musl/-/socket-patch-linux-x64-musl-4.0.0.tgz", + "integrity": "sha512-WdBLlNlaig163bYxOSywzFBHB6p/Mmn+Ld7lW0UdFYpt+05321RrlChG3zlC9C2wZpQPLvIi0LgxO7L7269Nrw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@socketsecurity/socket-patch-win32-arm64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-win32-arm64/-/socket-patch-win32-arm64-4.0.0.tgz", + "integrity": "sha512-2Erb95Am9T+yqDrmHS9bVfbKLK6qVOjqMNQMwrFF8lwg5Jn8LSl+N1fV7ZzNr5sVggWmjJ9wQ0LLNT6KPAUxyQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@socketsecurity/socket-patch-win32-ia32": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-win32-ia32/-/socket-patch-win32-ia32-4.0.0.tgz", + "integrity": "sha512-4wztPWOCk2sL3zdu405FYbVVRqSVf1lZt4xPzK8OTGvUUwhQls8Z1F15R1X7TdUxRnw6IDsA5DP2BZ1veOTfGg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@socketsecurity/socket-patch-win32-x64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@socketsecurity/socket-patch-win32-x64/-/socket-patch-win32-x64-4.0.0.tgz", + "integrity": "sha512-oK1jQGT8KJdnt5PMek7PDJnTBWr+XDpgvgbW/00ht7xOcjp+2HtSFULHCOWeKImSeXli10VHEGxO3/5OkHRcGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@types/node": { "version": "20.19.41", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",