From b4d642935b98be8a754c63aee9e5eeaf20bec1c5 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:07:44 +0100 Subject: [PATCH 01/57] ci(security): audit Rust dependencies on changes and weekly --- .github/workflows/security-audit.yml | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/security-audit.yml diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml new file mode 100644 index 0000000..d137c02 --- /dev/null +++ b/.github/workflows/security-audit.yml @@ -0,0 +1,41 @@ +name: Dependency Security Audit + +on: + pull_request: + paths: + - "Cargo.lock" + - "**/Cargo.toml" + - ".github/workflows/security-audit.yml" + push: + branches: [main] + paths: + - "Cargo.lock" + - "**/Cargo.toml" + - ".github/workflows/security-audit.yml" + schedule: + - cron: "23 4 * * 1" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: dependency-audit-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + audit: + name: RustSec advisory audit + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Install stable Rust + uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + - name: Install cargo-audit + run: cargo install cargo-audit --locked + - name: Audit locked dependencies + run: cargo audit --file Cargo.lock From 67de7784593d3508933a4da966a83aab91d59ac8 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:07:54 +0100 Subject: [PATCH 02/57] build: declare the tested minimum Rust version --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 32668e3..d7df0c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ [workspace.package] version = "0.1.0" edition = "2021" +rust-version = "1.91" license = "Apache-2.0" authors = ["StellarFoundry"] repository = "https://github.com/StellarFoundry/stellar-devkit" From 1946050d4463c1a4add2189517ddb8555ca59263 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:07:59 +0100 Subject: [PATCH 03/57] docs: align project status with MSRV and security CI --- docs/PROJECT_STATUS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index cffaf10..0421f49 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -18,7 +18,8 @@ - `devkit-rpc`: typed RPC protocol layer with an injectable transport, a deterministic mock, endpoint validation, typed health/network/latest-ledger results, and a retry policy. **No live network transport yet.** -- CI: fmt, clippy, tests on Linux/Windows/macOS. +- CI: fmt, clippy, tests on Linux/Windows/macOS, plus a Rust 1.91.0 MSRV job and scheduled fuzz smoke tests. +- Dependency security: a RustSec `cargo audit` workflow now runs for lockfile/manifest changes and weekly. - Docs: ecosystem research, architecture, getting started, CLI, roadmap, this file. - Governance: README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, LICENSE, @@ -49,7 +50,7 @@ - Decoding uses a permissive limit today; a stricter, configurable limit is planned. - No network functionality exists; `doctor` states this explicitly. -- No MSRV is pinned yet. +- The workspace declares Rust 1.91 as its minimum supported version; CI tests on Rust 1.91.0. - `devkit-core` has no configuration loader yet. ## Security status From 9d0f537f46765f8c8c1178643d567a12508d0890 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:08:59 +0100 Subject: [PATCH 04/57] docs: link Drips readiness checklist from README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e370771..c99b355 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Exit codes: `0` success, `2` usage error, `3` runtime/decode error. - [Project status](docs/PROJECT_STATUS.md) - [Roadmap](docs/ROADMAP.md) - [Contributing through Drips Wave](docs/WAVE.md) +- [Drips readiness checklist](docs/DRIPS_READINESS.md) ## Contributing From b05451bc3da55b6939a556678dc473f2924e24a2 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:09:23 +0100 Subject: [PATCH 05/57] ci: pin third-party workflow actions to immutable commits --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c13bd3e..712c8a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable with: components: rustfmt - run: cargo fmt --all -- --check @@ -32,11 +32,11 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable with: components: clippy - - uses: Swatinem/rust-cache@v2 + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 - run: cargo clippy --all-targets --all-features -- -D warnings test: @@ -47,7 +47,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 - run: cargo test --all --all-features From 140e3a547db4665c8195e83a070ea1c1c639e63e Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:09:26 +0100 Subject: [PATCH 06/57] ci(security): pin fuzz workflow actions to immutable commits --- .github/workflows/fuzz.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index a6cd259..277696f 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -24,9 +24,11 @@ jobs: name: Fuzz smoke runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@nightly - - uses: Swatinem/rust-cache@v2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # pinned action + with: + toolchain: nightly + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 with: workspaces: fuzz - name: Install cargo-fuzz From 294fbc7a6c520f01ab52f52ce55e3ec0d758f200 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:16:13 +0100 Subject: [PATCH 07/57] feat: implement RPC, WASM inspection, analysis, config and release validation --- .github/workflows/release-validation.yml | 24 +++++ Cargo.toml | 6 ++ README.md | 3 +- crates/devkit-analysis/Cargo.toml | 13 +++ crates/devkit-analysis/src/lib.rs | 100 ++++++++++++++++++++ crates/devkit-config/Cargo.toml | 12 +++ crates/devkit-config/src/lib.rs | 83 +++++++++++++++++ crates/devkit-contract/Cargo.toml | 13 +++ crates/devkit-contract/src/lib.rs | 113 +++++++++++++++++++++++ crates/devkit-rpc/Cargo.toml | 1 + crates/devkit-rpc/src/lib.rs | 81 ++++++++++++++++ docs/IMPLEMENTED_CAPABILITIES.md | 11 +++ 12 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/release-validation.yml create mode 100644 crates/devkit-analysis/Cargo.toml create mode 100644 crates/devkit-analysis/src/lib.rs create mode 100644 crates/devkit-config/Cargo.toml create mode 100644 crates/devkit-config/src/lib.rs create mode 100644 crates/devkit-contract/Cargo.toml create mode 100644 crates/devkit-contract/src/lib.rs create mode 100644 docs/IMPLEMENTED_CAPABILITIES.md diff --git a/.github/workflows/release-validation.yml b/.github/workflows/release-validation.yml new file mode 100644 index 0000000..b7fb3e8 --- /dev/null +++ b/.github/workflows/release-validation.yml @@ -0,0 +1,24 @@ +name: Release validation + +on: + push: + tags: ["v*.*.*"] + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + name: Validate release artifacts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v2 + - run: cargo fmt --all -- --check + - run: cargo clippy --workspace --all-targets --all-features -- -D warnings + - run: cargo test --workspace --all-features + - run: cargo package --workspace --no-verify diff --git a/Cargo.toml b/Cargo.toml index 32668e3..555ecec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ members = [ "crates/devkit-xdr", "crates/devkit-rpc", "crates/devkit-cli", + "crates/devkit-contract", + "crates/devkit-analysis", + "crates/devkit-config", ] [workspace.package] @@ -29,6 +32,9 @@ stellar-strkey = "0.0.18" devkit-core = { path = "crates/devkit-core", version = "0.1.0" } devkit-xdr = { path = "crates/devkit-xdr", version = "0.1.0" } devkit-rpc = { path = "crates/devkit-rpc", version = "0.1.0" } +devkit-contract = { path = "crates/devkit-contract", version = "0.1.0" } +devkit-analysis = { path = "crates/devkit-analysis", version = "0.1.0" } +devkit-config = { path = "crates/devkit-config", version = "0.1.0" } [profile.release] lto = "thin" diff --git a/README.md b/README.md index e370771..961a407 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,7 @@ The core works fully **offline** and never requires AI. - `stellar-foundry` CLI — `version`, `doctor`, `strkey`, `scval`, `envelope`, `event`. -Everything else (RPC, fixtures, contract inspection, security analysis) is -planned and tracked as issues; it is not implemented. +Library APIs now include bounded HTTP RPC transport, structural WASM inspection, deterministic structural findings with SARIF serialization, and validated TOML configuration. See [docs/IMPLEMENTED_CAPABILITIES.md](docs/IMPLEMENTED_CAPABILITIES.md) for exact scope and known limitations. CLI integration for these APIs remains planned. ## Install and build diff --git a/crates/devkit-analysis/Cargo.toml b/crates/devkit-analysis/Cargo.toml new file mode 100644 index 0000000..7ca0c4f --- /dev/null +++ b/crates/devkit-analysis/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "devkit-analysis" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true +description = "Deterministic structural security checks and SARIF output for Soroban WASM." + +[dependencies] +devkit-contract.workspace = true +serde.workspace = true +serde_json.workspace = true diff --git a/crates/devkit-analysis/src/lib.rs b/crates/devkit-analysis/src/lib.rs new file mode 100644 index 0000000..3f7f3af --- /dev/null +++ b/crates/devkit-analysis/src/lib.rs @@ -0,0 +1,100 @@ +//! Deterministic, evidence-backed structural checks for contract WASM. +//! Findings are indicators, not proof that a contract is vulnerable or malicious. + +use devkit_contract::ContractReport; +use serde::Serialize; +use serde_json::{json, Value}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum Severity { Error, Warning, Note } + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct Finding { + pub rule_id: String, + pub title: String, + pub severity: Severity, + pub message: String, + pub evidence: String, +} + +pub fn analyze(report: &ContractReport) -> Vec { + let mut findings = Vec::new(); + if !report.has_contract_spec { + findings.push(Finding { + rule_id: "DEVKIT-WASM-001".into(), + title: "Soroban contract specification is absent".into(), + severity: Severity::Warning, + message: "No contractspecv0 custom section was found; callers may not be able to inspect typed contract interfaces from this artifact.".into(), + evidence: format!("custom_sections={}", report.custom_sections.join(",")), + }); + } + for import in report.imports.iter().filter(|item| item.module != "env") { + findings.push(Finding { + rule_id: "DEVKIT-WASM-002".into(), + title: "Import from a non-standard module".into(), + severity: Severity::Warning, + message: "The module imports a symbol from a module other than env. Verify this import is expected for the target runtime.".into(), + evidence: format!("{}::{}", import.module, import.name), + }); + } + if report.wasm_bytes > 1024 * 1024 { + findings.push(Finding { + rule_id: "DEVKIT-WASM-003".into(), + title: "Large WASM artifact".into(), + severity: Severity::Note, + message: "The WASM artifact exceeds 1 MiB. Review size and deployment/resource constraints; size alone is not a vulnerability.".into(), + evidence: format!("wasm_bytes={}", report.wasm_bytes), + }); + } + findings +} + +pub fn to_sarif(findings: &[Finding], artifact_uri: Option<&str>) -> Value { + let rules = vec![ + json!({"id":"DEVKIT-WASM-001","name":"MissingContractSpec","shortDescription":{"text":"Soroban contract specification is absent"},"defaultConfiguration":{"level":"warning"}}), + json!({"id":"DEVKIT-WASM-002","name":"NonStandardImport","shortDescription":{"text":"Import from a non-standard module"},"defaultConfiguration":{"level":"warning"}}), + json!({"id":"DEVKIT-WASM-003","name":"LargeWasmArtifact","shortDescription":{"text":"Large WASM artifact"},"defaultConfiguration":{"level":"note"}}), + ]; + let results: Vec = findings.iter().map(|finding| { + let level = match finding.severity { Severity::Error => "error", Severity::Warning => "warning", Severity::Note => "note" }; + let mut result = json!({ + "ruleId": finding.rule_id, + "level": level, + "message": {"text": finding.message}, + "properties": {"title": finding.title, "evidence": finding.evidence} + }); + if let Some(uri) = artifact_uri { + result["locations"] = json!([{"physicalLocation":{"artifactLocation":{"uri":uri}}}]); + } + result + }).collect(); + json!({ + "$schema":"https://json.schemastore.org/sarif-2.1.0.json", + "version":"2.1.0", + "runs":[{"tool":{"driver":{"name":"stellar-foundry","informationUri":"https://github.com/StellarFoundry/stellar-devkit","rules":rules}},"results":results}] + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use devkit_contract::{ContractReport, ImportInfo}; + + #[test] + fn emits_evidence_for_nonstandard_imports() { + let report = ContractReport { + wasm_bytes: 100, + imports: vec![ImportInfo { module: "host".into(), name: "call".into() }], + exports: vec![], + custom_sections: vec![], + has_contract_spec: false, + contract_spec_bytes: 0, + }; + let findings = analyze(&report); + assert!(findings.iter().any(|item| item.rule_id == "DEVKIT-WASM-002" && item.evidence == "host::call")); + let sarif = to_sarif(&findings, Some("contract.wasm")); + assert_eq!(sarif["version"], "2.1.0"); + assert!(sarif["runs"][0]["results"].as_array().unwrap().len() >= 2); + } +} diff --git a/crates/devkit-config/Cargo.toml b/crates/devkit-config/Cargo.toml new file mode 100644 index 0000000..db1f4d2 --- /dev/null +++ b/crates/devkit-config/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "devkit-config" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true +description = "Validated configuration and secret-safe diagnostics for StellarFoundry DevKit." + +[dependencies] +serde.workspace = true +toml.workspace = true diff --git a/crates/devkit-config/src/lib.rs b/crates/devkit-config/src/lib.rs new file mode 100644 index 0000000..8bbe3ae --- /dev/null +++ b/crates/devkit-config/src/lib.rs @@ -0,0 +1,83 @@ +//! Configuration parsing and secret-safe diagnostics. +//! Configuration is loaded from TOML text; environment variables can override +//! endpoint, timeout and verbosity without printing their values. + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default, deny_unknown_fields)] +pub struct Config { + pub rpc_url: Option, + pub timeout_ms: u64, + pub verbose: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { rpc_url: None, timeout_ms: 10_000, verbose: false } + } +} + +impl Config { + pub fn parse_toml(input: &str) -> Result { + let config: Self = toml::from_str(input).map_err(|error| format!("invalid config: {error}"))?; + config.validate()?; + Ok(config) + } + + pub fn validate(&self) -> Result<(), String> { + if !(100..=120_000).contains(&self.timeout_ms) { + return Err("timeout_ms must be between 100 and 120000".into()); + } + if let Some(url) = &self.rpc_url { + if url.trim().is_empty() { + return Err("rpc_url must not be empty".into()); + } + } + Ok(()) + } + + pub fn apply_environment(&mut self) -> Result<(), String> { + if let Ok(value) = std::env::var("STELLAR_FOUNDRY_RPC_URL") { + self.rpc_url = Some(value); + } + if let Ok(value) = std::env::var("STELLAR_FOUNDRY_TIMEOUT_MS") { + self.timeout_ms = value.parse().map_err(|_| "STELLAR_FOUNDRY_TIMEOUT_MS must be an integer".to_string())?; + } + if let Ok(value) = std::env::var("STELLAR_FOUNDRY_VERBOSE") { + self.verbose = match value.to_ascii_lowercase().as_str() { + "1" | "true" | "yes" => true, + "0" | "false" | "no" => false, + _ => return Err("STELLAR_FOUNDRY_VERBOSE must be true/false".into()), + }; + } + self.validate() + } +} + +/// Emit a static diagnostic event only when verbose mode is enabled. +/// Callers should pass event names, never URLs, headers, payloads or secrets. +pub fn diagnostic(verbose: bool, event: &'static str) { + if verbose { + eprintln!("stellar-foundry: {event}"); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_defaults_and_explicit_values() { + let config = Config::parse_toml("timeout_ms = 5000\nverbose = true").unwrap(); + assert_eq!(config.timeout_ms, 5000); + assert!(config.verbose); + assert_eq!(config.rpc_url, None); + } + + #[test] + fn rejects_unknown_keys_and_unsafe_timeouts() { + assert!(Config::parse_toml("mystery = 1").is_err()); + assert!(Config::parse_toml("timeout_ms = 1").is_err()); + } +} diff --git a/crates/devkit-contract/Cargo.toml b/crates/devkit-contract/Cargo.toml new file mode 100644 index 0000000..98820a5 --- /dev/null +++ b/crates/devkit-contract/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "devkit-contract" +version.workspace = true +edition.workspace = true +license.workspace = true +authors.workspace = true +repository.workspace = true +description = "Bounded, non-executing inspection of Soroban contract WASM artifacts." + +[dependencies] +devkit-core.workspace = true +serde.workspace = true +wasmparser.workspace = true diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs new file mode 100644 index 0000000..b93163a --- /dev/null +++ b/crates/devkit-contract/src/lib.rs @@ -0,0 +1,113 @@ +//! Bounded structural inspection for untrusted Soroban contract WASM. +//! +//! This crate validates the WASM container but never executes it. The +//! contractspecv0 custom section is detected and sized; decoding individual +//! XDR specification entries is intentionally reported as a limitation until +//! a versioned spec model is added. + +use devkit_core::DevkitError; +use serde::Serialize; +use wasmparser::{Parser, Payload, Validator}; + +pub const MAX_WASM_BYTES: usize = 16 * 1024 * 1024; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct ImportInfo { + pub module: String, + pub name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct ExportInfo { + pub name: String, + pub kind: String, + pub index: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct ContractReport { + pub wasm_bytes: usize, + pub imports: Vec, + pub exports: Vec, + pub custom_sections: Vec, + pub has_contract_spec: bool, + pub contract_spec_bytes: usize, +} + +pub fn inspect_wasm(bytes: &[u8]) -> Result { + if bytes.is_empty() { + return Err(DevkitError::invalid("WASM input is empty")); + } + if bytes.len() > MAX_WASM_BYTES { + return Err(DevkitError::invalid(format!("WASM exceeds the {MAX_WASM_BYTES}-byte limit"))); + } + Validator::new().validate_all(bytes) + .map_err(|error| DevkitError::decode(format!("invalid WASM module: {error}")))?; + let mut report = ContractReport { + wasm_bytes: bytes.len(), + imports: Vec::new(), + exports: Vec::new(), + custom_sections: Vec::new(), + has_contract_spec: false, + contract_spec_bytes: 0, + }; + for payload in Parser::new(0).parse_all(bytes) { + match payload.map_err(DevkitError::decode)? { + Payload::ImportSection(reader) => { + for import in reader { + let import = import.map_err(DevkitError::decode)?; + report.imports.push(ImportInfo { + module: import.module.to_owned(), + name: import.name.to_owned(), + }); + } + } + Payload::ExportSection(reader) => { + for export in reader { + let export = export.map_err(DevkitError::decode)?; + report.exports.push(ExportInfo { + name: export.name.to_owned(), + kind: format!("{:?}", export.kind), + index: export.index, + }); + } + } + Payload::CustomSection(section) => { + let name = section.name().to_owned(); + if name == "contractspecv0" { + report.has_contract_spec = true; + report.contract_spec_bytes = section.data().len(); + } + report.custom_sections.push(name); + } + _ => {} + } + } + Ok(report) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rejects_empty_and_non_wasm_inputs() { + assert!(inspect_wasm(&[]).is_err()); + assert!(inspect_wasm(b"not wasm").is_err()); + } + + #[test] + fn rejects_oversized_input_before_parsing() { + let input = vec![0; MAX_WASM_BYTES + 1]; + assert!(inspect_wasm(&input).unwrap_err().to_string().contains("limit")); + } + + #[test] + fn accepts_a_minimal_valid_wasm_module() { + let report = inspect_wasm(b"\0asm\x01\0\0\0").unwrap(); + assert_eq!(report.wasm_bytes, 8); + assert!(report.imports.is_empty()); + assert!(report.exports.is_empty()); + assert!(!report.has_contract_spec); + } +} diff --git a/crates/devkit-rpc/Cargo.toml b/crates/devkit-rpc/Cargo.toml index 27e26c8..d2bf3c9 100644 --- a/crates/devkit-rpc/Cargo.toml +++ b/crates/devkit-rpc/Cargo.toml @@ -12,3 +12,4 @@ devkit-core.workspace = true serde.workspace = true serde_json.workspace = true url = "2" +reqwest.workspace = true diff --git a/crates/devkit-rpc/src/lib.rs b/crates/devkit-rpc/src/lib.rs index 466dae6..bce5624 100644 --- a/crates/devkit-rpc/src/lib.rs +++ b/crates/devkit-rpc/src/lib.rs @@ -15,6 +15,9 @@ use std::cell::RefCell; use std::collections::VecDeque; +use std::io::Read; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::Duration; use devkit_core::DevkitError; use serde::{Deserialize, Serialize}; @@ -149,6 +152,84 @@ impl Transport for MockTransport { } } + +/// A bounded HTTP JSON-RPC transport for a Stellar RPC endpoint. +/// +/// Responses are capped before JSON parsing. Requests have a finite timeout, +/// JSON-RPC identifiers are checked, and server error messages are returned as +/// structured errors. Redirects are disabled by the HTTP client to avoid +/// silently forwarding RPC requests to an unexpected host. +pub struct HttpTransport { + endpoint: String, + client: reqwest::blocking::Client, + next_id: AtomicU64, +} + +impl HttpTransport { + /// Creates a transport with an explicit request timeout. + pub fn new(endpoint: Endpoint, timeout: Duration) -> Result { + if timeout.is_zero() { + return Err(DevkitError::invalid("RPC timeout must be greater than zero")); + } + let client = reqwest::blocking::Client::builder() + .timeout(timeout) + .redirect(reqwest::redirect::Policy::none()) + .build() + .map_err(|error| DevkitError::Io(format!("failed to build RPC HTTP client: {error}")))?; + Ok(Self { + endpoint: endpoint.as_str().to_owned(), + client, + next_id: AtomicU64::new(1), + }) + } + + /// Creates a transport with a 10-second request timeout. + pub fn with_default_timeout(endpoint: Endpoint) -> Result { + Self::new(endpoint, Duration::from_secs(10)) + } +} + +impl Transport for HttpTransport { + fn call(&self, method: &str, params: Value) -> Result { + if method.trim().is_empty() { + return Err(DevkitError::invalid("RPC method must not be empty")); + } + let id = self.next_id.fetch_add(1, Ordering::Relaxed); + let request = json!({ + "jsonrpc": "2.0", + "id": id, + "method": method, + "params": params + }); + let response = self.client.post(&self.endpoint).json(&request).send() + .map_err(|error| DevkitError::Io(format!("RPC HTTP request failed: {error}")))?; + let status = response.status(); + let mut body = Vec::new(); + response.take((MAX_RPC_RESPONSE_BYTES + 1) as u64).read_to_end(&mut body) + .map_err(|error| DevkitError::Io(format!("failed reading RPC response: {error}")))?; + if body.len() > MAX_RPC_RESPONSE_BYTES { + return Err(response_too_large()); + } + if !status.is_success() { + return Err(DevkitError::Io(format!("RPC endpoint returned HTTP status {status}"))); + } + let value: Value = serde_json::from_slice(&body).map_err(DevkitError::decode)?; + if value.get("jsonrpc").and_then(Value::as_str) != Some("2.0") { + return Err(DevkitError::decode("RPC response is missing JSON-RPC version 2.0")); + } + if value.get("id").and_then(Value::as_u64) != Some(id) { + return Err(DevkitError::decode("RPC response identifier does not match request")); + } + if let Some(error) = value.get("error") { + let code = error.get("code").and_then(Value::as_i64).unwrap_or_default(); + let message = error.get("message").and_then(Value::as_str).unwrap_or("unknown RPC error"); + return Err(DevkitError::decode(format!("RPC error {code}: {message}"))); + } + value.get("result").cloned() + .ok_or_else(|| DevkitError::decode("RPC response has neither result nor error")) + } +} + /// Well-known result of `getHealth`. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/docs/IMPLEMENTED_CAPABILITIES.md b/docs/IMPLEMENTED_CAPABILITIES.md new file mode 100644 index 0000000..4eac8ab --- /dev/null +++ b/docs/IMPLEMENTED_CAPABILITIES.md @@ -0,0 +1,11 @@ +# Implemented library capabilities + +This branch adds reusable library APIs for the remaining developer-tooling workstreams: + +- **RPC transport:** `devkit_rpc::HttpTransport` sends JSON-RPC POST requests with a finite timeout, disabled redirects, request ID validation, and a response byte cap enforced before JSON parsing. Endpoint policy continues to allow HTTPS and loopback-only HTTP. +- **WASM inspection:** `devkit_contract::inspect_wasm` validates the WASM container without executing it, and reports imports, exports, custom sections, and the size/presence of Soroban's `contractspecv0` section. It does not yet decode each XDR spec entry into typed functions/structs/enums. +- **Analysis and SARIF:** `devkit_analysis::analyze` emits deterministic findings with stable rule IDs and evidence. `to_sarif` produces SARIF 2.1.0. Current rules are structural heuristics, not proof of a vulnerability. +- **Configuration/diagnostics:** `devkit_config::Config` parses strict TOML, validates timeouts, and supports environment overrides. Verbose diagnostics accept static event names and avoid printing endpoints or payloads. +- **Release readiness:** the release-validation workflow runs tests, formatting/lint checks, and package validation on version tags. + +These APIs are library-first in this change; CLI subcommands and GitHub code-scanning upload automation remain follow-up integration work. No WASM is executed and no network request is made unless a caller explicitly constructs and uses the HTTP transport. From 717ef40a1b8684919b924435e991f69b9ae3a5c4 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:16:42 +0100 Subject: [PATCH 08/57] fix: declare transport and wasm parser workspace dependencies --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 555ecec..f829aa0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ anyhow = "1" clap = { version = "4", features = ["derive"] } stellar-xdr = { version = "28.0.0", features = ["serde", "base64", "alloc"] } stellar-strkey = "0.0.18" +reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] } +wasmparser = "0.219" devkit-core = { path = "crates/devkit-core", version = "0.1.0" } devkit-xdr = { path = "crates/devkit-xdr", version = "0.1.0" } From 50b1bc92a33e9e7c1ff1e2b508a7dcd20a874b18 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:07 +0100 Subject: [PATCH 09/57] docs: add release readiness checklist --- docs/RELEASE_CHECKLIST.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/RELEASE_CHECKLIST.md diff --git a/docs/RELEASE_CHECKLIST.md b/docs/RELEASE_CHECKLIST.md new file mode 100644 index 0000000..c0e54f0 --- /dev/null +++ b/docs/RELEASE_CHECKLIST.md @@ -0,0 +1,13 @@ +# Release checklist + +Before publishing a version: + +1. Confirm the version and changelog entries agree. +2. Run `cargo fmt --all -- --check`. +3. Run `cargo clippy --workspace --all-targets --all-features -- -D warnings`. +4. Run `cargo test --workspace --all-features` on Linux, Windows, and macOS. +5. Run `cargo package --workspace --no-verify` and review packaging warnings. +6. Confirm the lockfile is regenerated and committed after dependency changes. +7. Review the generated SARIF against the SARIF 2.1.0 schema. +8. Verify artifacts and checksums before publishing; do not include credentials or local configuration. +9. State known limitations in the release notes. Structural analysis findings are heuristics and do not prove a vulnerability. From aa333c4ec8ea98dd47721d15dbf3fc198779feb5 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:20 +0100 Subject: [PATCH 10/57] fix: make finding severity copyable for SARIF mapping From bf7cb8826a08adbeb91fe5d855f1026e624f3069 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:35 +0100 Subject: [PATCH 11/57] feat(cli): depend on contract, analysis and config libraries --- crates/devkit-cli/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/devkit-cli/Cargo.toml b/crates/devkit-cli/Cargo.toml index 42b434b..e886cfa 100644 --- a/crates/devkit-cli/Cargo.toml +++ b/crates/devkit-cli/Cargo.toml @@ -17,3 +17,7 @@ devkit-xdr.workspace = true devkit-rpc.workspace = true clap.workspace = true serde_json.workspace = true + +devkit-contract.workspace = true +devkit-analysis.workspace = true +devkit-config.workspace = true From 5058871203284de8d961314d3094e1dfef644497 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:39 +0100 Subject: [PATCH 12/57] feat(cli): expose WASM analysis and live RPC health commands --- crates/devkit-cli/src/main.rs | 56 ++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index d8591bf..d768bf6 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -6,6 +6,7 @@ //! * `3` runtime error (decode failure, I/O) use std::{ + fs, io::{self, IsTerminal, Read}, process::ExitCode, }; @@ -23,13 +24,14 @@ const MAX_STDIN_BYTES: usize = 16 * 1024 * 1024; enum FormatArg { Terminal, Json, + Sarif, } impl From for OutputFormat { fn from(value: FormatArg) -> Self { match value { FormatArg::Terminal => OutputFormat::Terminal, - FormatArg::Json => OutputFormat::Json, + FormatArg::Json | FormatArg::Sarif => OutputFormat::Json, } } } @@ -77,6 +79,12 @@ enum Command { /// The endpoint URL to validate. url: String, }, + /// Inspect a WASM module without executing it. + InspectWasm { path: String }, + /// Run structural checks against a WASM module; --format sarif emits SARIF 2.1.0. + AnalyzeWasm { path: String }, + /// Query getHealth from an explicitly supplied Stellar RPC endpoint. + RpcHealth { endpoint: String }, } #[derive(Debug)] @@ -191,6 +199,34 @@ fn run(cli: Cli) -> Result { print_value(format, &xdr::summary("contract_event", &decoded)); Ok(EXIT_SUCCESS) } + Command::InspectWasm { path } => { + let bytes = read_wasm(&path)?; + let report = devkit_contract::inspect_wasm(&bytes).map_err(runtime)?; + let value = serde_json::to_value(&report) + .map_err(|error| CliError::new(EXIT_ERROR, "internal", error.to_string()))?; + print_value(format, &value); + Ok(EXIT_SUCCESS) + } + Command::AnalyzeWasm { path } => { + let bytes = read_wasm(&path)?; + let report = devkit_contract::inspect_wasm(&bytes).map_err(runtime)?; + let findings = devkit_analysis::analyze(&report); + if cli.format == FormatArg::Sarif { + println!("{}", devkit_analysis::to_sarif(&findings, Some(&path))); + } else { + print_value(format, &serde_json::json!({ "findings": findings, "count": findings.len() })); + } + Ok(EXIT_SUCCESS) + } + Command::RpcHealth { endpoint } => { + let endpoint = devkit_rpc::Endpoint::parse(&endpoint).map_err(runtime)?; + let transport = devkit_rpc::HttpTransport::with_default_timeout(endpoint).map_err(runtime)?; + let health = devkit_rpc::Client::new(transport).health().map_err(runtime)?; + let value = serde_json::to_value(health) + .map_err(|error| CliError::new(EXIT_ERROR, "internal", error.to_string()))?; + print_value(format, &value); + Ok(EXIT_SUCCESS) + } Command::Endpoint { url } => { let endpoint = devkit_rpc::Endpoint::parse(&url).map_err(runtime)?; let report = serde_json::json!({ @@ -218,14 +254,26 @@ fn doctor(format: OutputFormat) { let report = serde_json::json!({ "tool": TOOL_NAME, "version": VERSION, - "capabilities": ["strkey", "scval", "transaction_envelope", "contract_event", "endpoint_validation"], + "capabilities": ["strkey", "scval", "transaction_envelope", "contract_event", "endpoint_validation", "wasm_inspection", "wasm_analysis", "rpc_health"], "networks": networks, - "network_access": "none (offline decoding only)", - "rpc_transport": "mock only (no live transport yet)", + "network_access": "explicit only (rpc-health command)", + "rpc_transport": "bounded HTTP JSON-RPC and deterministic mock", }); print_value(format, &report); } +fn read_wasm(path: &str) -> Result, CliError> { + let metadata = fs::metadata(path).map_err(|error| CliError::new(EXIT_ERROR, "io", format!("failed to inspect WASM file: {error}")))?; + if metadata.len() > devkit_contract::MAX_WASM_BYTES as u64 { + return Err(CliError::new(EXIT_ERROR, "invalid_input", format!("WASM exceeds the {}-byte limit", devkit_contract::MAX_WASM_BYTES))); + } + let bytes = fs::read(path).map_err(|error| CliError::new(EXIT_ERROR, "io", format!("failed to read WASM file: {error}")))?; + if bytes.len() > devkit_contract::MAX_WASM_BYTES { + return Err(CliError::new(EXIT_ERROR, "invalid_input", format!("WASM exceeds the {}-byte limit", devkit_contract::MAX_WASM_BYTES))); + } + Ok(bytes) +} + /// Resolve a decode argument, reading bounded UTF-8 input only when explicitly asked. fn read_value(value: String) -> Result { if value != "-" { From c6d185ffa8a5e15d62cd48bcf810e293eb1a71d1 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:42 +0100 Subject: [PATCH 13/57] docs: document WASM and RPC CLI commands --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 961a407..c9ef1ec 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ The core works fully **offline** and never requires AI. - `stellar-foundry` CLI — `version`, `doctor`, `strkey`, `scval`, `envelope`, `event`. -Library APIs now include bounded HTTP RPC transport, structural WASM inspection, deterministic structural findings with SARIF serialization, and validated TOML configuration. See [docs/IMPLEMENTED_CAPABILITIES.md](docs/IMPLEMENTED_CAPABILITIES.md) for exact scope and known limitations. CLI integration for these APIs remains planned. +Library APIs and CLI commands now include bounded HTTP RPC health checks, structural WASM inspection, deterministic findings with SARIF serialization, and validated TOML configuration. See [docs/IMPLEMENTED_CAPABILITIES.md](docs/IMPLEMENTED_CAPABILITIES.md) for exact scope and known limitations. ## Install and build @@ -37,6 +37,9 @@ stellar-foundry strkey CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE stellar-foundry scval --format json stellar-foundry envelope --format json stellar-foundry event --format json +stellar-foundry inspect-wasm ./contract.wasm +stellar-foundry analyze-wasm --format sarif ./contract.wasm +stellar-foundry rpc-health https://soroban-testnet.stellar.org ``` Exit codes: `0` success, `2` usage error, `3` runtime/decode error. From c6feb6dfd1f77f378960aeef2078b0b8c40d5fd3 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:47 +0100 Subject: [PATCH 14/57] docs: clarify CLI and SARIF integration status --- docs/IMPLEMENTED_CAPABILITIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/IMPLEMENTED_CAPABILITIES.md b/docs/IMPLEMENTED_CAPABILITIES.md index 4eac8ab..fbf2e13 100644 --- a/docs/IMPLEMENTED_CAPABILITIES.md +++ b/docs/IMPLEMENTED_CAPABILITIES.md @@ -8,4 +8,4 @@ This branch adds reusable library APIs for the remaining developer-tooling works - **Configuration/diagnostics:** `devkit_config::Config` parses strict TOML, validates timeouts, and supports environment overrides. Verbose diagnostics accept static event names and avoid printing endpoints or payloads. - **Release readiness:** the release-validation workflow runs tests, formatting/lint checks, and package validation on version tags. -These APIs are library-first in this change; CLI subcommands and GitHub code-scanning upload automation remain follow-up integration work. No WASM is executed and no network request is made unless a caller explicitly constructs and uses the HTTP transport. +CLI commands are available as `inspect-wasm`, `analyze-wasm`, and `rpc-health`. GitHub code-scanning upload automation and decoding each XDR contract spec entry remain follow-up integration work. No WASM is executed and no network request is made unless a caller explicitly constructs and uses the HTTP transport. From 003a5064212c00ad9e8a79b8bcf0b631de01e90f Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:17:58 +0100 Subject: [PATCH 15/57] fix(cli): avoid borrowing findings after moving them into JSON --- crates/devkit-cli/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index d768bf6..b68f48e 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -214,7 +214,8 @@ fn run(cli: Cli) -> Result { if cli.format == FormatArg::Sarif { println!("{}", devkit_analysis::to_sarif(&findings, Some(&path))); } else { - print_value(format, &serde_json::json!({ "findings": findings, "count": findings.len() })); + let count = findings.len(); + print_value(format, &serde_json::json!({ "findings": findings, "count": count })); } Ok(EXIT_SUCCESS) } From 5cc975129b1a87e9b335fbb890fca61be3438433 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:19:55 +0100 Subject: [PATCH 16/57] style: format analysis and SARIF implementation --- crates/devkit-analysis/src/lib.rs | 49 +++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/crates/devkit-analysis/src/lib.rs b/crates/devkit-analysis/src/lib.rs index 3f7f3af..8647a04 100644 --- a/crates/devkit-analysis/src/lib.rs +++ b/crates/devkit-analysis/src/lib.rs @@ -7,7 +7,11 @@ use serde_json::{json, Value}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] #[serde(rename_all = "lowercase")] -pub enum Severity { Error, Warning, Note } +pub enum Severity { + Error, + Warning, + Note, +} #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct Finding { @@ -56,19 +60,27 @@ pub fn to_sarif(findings: &[Finding], artifact_uri: Option<&str>) -> Value { json!({"id":"DEVKIT-WASM-002","name":"NonStandardImport","shortDescription":{"text":"Import from a non-standard module"},"defaultConfiguration":{"level":"warning"}}), json!({"id":"DEVKIT-WASM-003","name":"LargeWasmArtifact","shortDescription":{"text":"Large WASM artifact"},"defaultConfiguration":{"level":"note"}}), ]; - let results: Vec = findings.iter().map(|finding| { - let level = match finding.severity { Severity::Error => "error", Severity::Warning => "warning", Severity::Note => "note" }; - let mut result = json!({ - "ruleId": finding.rule_id, - "level": level, - "message": {"text": finding.message}, - "properties": {"title": finding.title, "evidence": finding.evidence} - }); - if let Some(uri) = artifact_uri { - result["locations"] = json!([{"physicalLocation":{"artifactLocation":{"uri":uri}}}]); - } - result - }).collect(); + let results: Vec = findings + .iter() + .map(|finding| { + let level = match finding.severity { + Severity::Error => "error", + Severity::Warning => "warning", + Severity::Note => "note", + }; + let mut result = json!({ + "ruleId": finding.rule_id, + "level": level, + "message": {"text": finding.message}, + "properties": {"title": finding.title, "evidence": finding.evidence} + }); + if let Some(uri) = artifact_uri { + result["locations"] = + json!([{"physicalLocation":{"artifactLocation":{"uri":uri}}}]); + } + result + }) + .collect(); json!({ "$schema":"https://json.schemastore.org/sarif-2.1.0.json", "version":"2.1.0", @@ -85,14 +97,19 @@ mod tests { fn emits_evidence_for_nonstandard_imports() { let report = ContractReport { wasm_bytes: 100, - imports: vec![ImportInfo { module: "host".into(), name: "call".into() }], + imports: vec![ImportInfo { + module: "host".into(), + name: "call".into(), + }], exports: vec![], custom_sections: vec![], has_contract_spec: false, contract_spec_bytes: 0, }; let findings = analyze(&report); - assert!(findings.iter().any(|item| item.rule_id == "DEVKIT-WASM-002" && item.evidence == "host::call")); + assert!(findings + .iter() + .any(|item| item.rule_id == "DEVKIT-WASM-002" && item.evidence == "host::call")); let sarif = to_sarif(&findings, Some("contract.wasm")); assert_eq!(sarif["version"], "2.1.0"); assert!(sarif["runs"][0]["results"].as_array().unwrap().len() >= 2); From 00a3e337daa0458174e0b62b2748635d39656e79 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:19:58 +0100 Subject: [PATCH 17/57] style: format new CLI command handlers --- crates/devkit-cli/src/main.rs | 37 +++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index b68f48e..be30e58 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -215,14 +215,20 @@ fn run(cli: Cli) -> Result { println!("{}", devkit_analysis::to_sarif(&findings, Some(&path))); } else { let count = findings.len(); - print_value(format, &serde_json::json!({ "findings": findings, "count": count })); + print_value( + format, + &serde_json::json!({ "findings": findings, "count": count }), + ); } Ok(EXIT_SUCCESS) } Command::RpcHealth { endpoint } => { let endpoint = devkit_rpc::Endpoint::parse(&endpoint).map_err(runtime)?; - let transport = devkit_rpc::HttpTransport::with_default_timeout(endpoint).map_err(runtime)?; - let health = devkit_rpc::Client::new(transport).health().map_err(runtime)?; + let transport = + devkit_rpc::HttpTransport::with_default_timeout(endpoint).map_err(runtime)?; + let health = devkit_rpc::Client::new(transport) + .health() + .map_err(runtime)?; let value = serde_json::to_value(health) .map_err(|error| CliError::new(EXIT_ERROR, "internal", error.to_string()))?; print_value(format, &value); @@ -264,11 +270,30 @@ fn doctor(format: OutputFormat) { } fn read_wasm(path: &str) -> Result, CliError> { - let metadata = fs::metadata(path).map_err(|error| CliError::new(EXIT_ERROR, "io", format!("failed to inspect WASM file: {error}")))?; + let metadata = fs::metadata(path).map_err(|error| { + CliError::new( + EXIT_ERROR, + "io", + format!("failed to inspect WASM file: {error}"), + ) + })?; if metadata.len() > devkit_contract::MAX_WASM_BYTES as u64 { - return Err(CliError::new(EXIT_ERROR, "invalid_input", format!("WASM exceeds the {}-byte limit", devkit_contract::MAX_WASM_BYTES))); + return Err(CliError::new( + EXIT_ERROR, + "invalid_input", + format!( + "WASM exceeds the {}-byte limit", + devkit_contract::MAX_WASM_BYTES + ), + )); } - let bytes = fs::read(path).map_err(|error| CliError::new(EXIT_ERROR, "io", format!("failed to read WASM file: {error}")))?; + let bytes = fs::read(path).map_err(|error| { + CliError::new( + EXIT_ERROR, + "io", + format!("failed to read WASM file: {error}"), + ) + })?; if bytes.len() > devkit_contract::MAX_WASM_BYTES { return Err(CliError::new(EXIT_ERROR, "invalid_input", format!("WASM exceeds the {}-byte limit", devkit_contract::MAX_WASM_BYTES))); } From 450fc1186c4c08c0b79a1a7e482eb06cb184e87e Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:20:01 +0100 Subject: [PATCH 18/57] style: format config implementation --- crates/devkit-config/src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/devkit-config/src/lib.rs b/crates/devkit-config/src/lib.rs index 8bbe3ae..bf226af 100644 --- a/crates/devkit-config/src/lib.rs +++ b/crates/devkit-config/src/lib.rs @@ -14,13 +14,18 @@ pub struct Config { impl Default for Config { fn default() -> Self { - Self { rpc_url: None, timeout_ms: 10_000, verbose: false } + Self { + rpc_url: None, + timeout_ms: 10_000, + verbose: false, + } } } impl Config { pub fn parse_toml(input: &str) -> Result { - let config: Self = toml::from_str(input).map_err(|error| format!("invalid config: {error}"))?; + let config: Self = + toml::from_str(input).map_err(|error| format!("invalid config: {error}"))?; config.validate()?; Ok(config) } @@ -42,7 +47,9 @@ impl Config { self.rpc_url = Some(value); } if let Ok(value) = std::env::var("STELLAR_FOUNDRY_TIMEOUT_MS") { - self.timeout_ms = value.parse().map_err(|_| "STELLAR_FOUNDRY_TIMEOUT_MS must be an integer".to_string())?; + self.timeout_ms = value + .parse() + .map_err(|_| "STELLAR_FOUNDRY_TIMEOUT_MS must be an integer".to_string())?; } if let Ok(value) = std::env::var("STELLAR_FOUNDRY_VERBOSE") { self.verbose = match value.to_ascii_lowercase().as_str() { From 800e00a059f5452ade97a3197ae39440125c6f1a Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:20:04 +0100 Subject: [PATCH 19/57] style: format WASM inspection implementation --- crates/devkit-contract/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs index b93163a..db342e0 100644 --- a/crates/devkit-contract/src/lib.rs +++ b/crates/devkit-contract/src/lib.rs @@ -39,9 +39,12 @@ pub fn inspect_wasm(bytes: &[u8]) -> Result { return Err(DevkitError::invalid("WASM input is empty")); } if bytes.len() > MAX_WASM_BYTES { - return Err(DevkitError::invalid(format!("WASM exceeds the {MAX_WASM_BYTES}-byte limit"))); + return Err(DevkitError::invalid(format!( + "WASM exceeds the {MAX_WASM_BYTES}-byte limit" + ))); } - Validator::new().validate_all(bytes) + Validator::new() + .validate_all(bytes) .map_err(|error| DevkitError::decode(format!("invalid WASM module: {error}")))?; let mut report = ContractReport { wasm_bytes: bytes.len(), @@ -99,7 +102,10 @@ mod tests { #[test] fn rejects_oversized_input_before_parsing() { let input = vec![0; MAX_WASM_BYTES + 1]; - assert!(inspect_wasm(&input).unwrap_err().to_string().contains("limit")); + assert!(inspect_wasm(&input) + .unwrap_err() + .to_string() + .contains("limit")); } #[test] From 22d2f07e30a8497eb47aa76d8c014f5c34c31f25 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:20:07 +0100 Subject: [PATCH 20/57] style: format bounded HTTP RPC transport --- crates/devkit-rpc/src/lib.rs | 45 +++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/crates/devkit-rpc/src/lib.rs b/crates/devkit-rpc/src/lib.rs index bce5624..1f514da 100644 --- a/crates/devkit-rpc/src/lib.rs +++ b/crates/devkit-rpc/src/lib.rs @@ -152,7 +152,6 @@ impl Transport for MockTransport { } } - /// A bounded HTTP JSON-RPC transport for a Stellar RPC endpoint. /// /// Responses are capped before JSON parsing. Requests have a finite timeout, @@ -169,13 +168,17 @@ impl HttpTransport { /// Creates a transport with an explicit request timeout. pub fn new(endpoint: Endpoint, timeout: Duration) -> Result { if timeout.is_zero() { - return Err(DevkitError::invalid("RPC timeout must be greater than zero")); + return Err(DevkitError::invalid( + "RPC timeout must be greater than zero", + )); } let client = reqwest::blocking::Client::builder() .timeout(timeout) .redirect(reqwest::redirect::Policy::none()) .build() - .map_err(|error| DevkitError::Io(format!("failed to build RPC HTTP client: {error}")))?; + .map_err(|error| { + DevkitError::Io(format!("failed to build RPC HTTP client: {error}")) + })?; Ok(Self { endpoint: endpoint.as_str().to_owned(), client, @@ -201,31 +204,51 @@ impl Transport for HttpTransport { "method": method, "params": params }); - let response = self.client.post(&self.endpoint).json(&request).send() + let response = self + .client + .post(&self.endpoint) + .json(&request) + .send() .map_err(|error| DevkitError::Io(format!("RPC HTTP request failed: {error}")))?; let status = response.status(); let mut body = Vec::new(); - response.take((MAX_RPC_RESPONSE_BYTES + 1) as u64).read_to_end(&mut body) + response + .take((MAX_RPC_RESPONSE_BYTES + 1) as u64) + .read_to_end(&mut body) .map_err(|error| DevkitError::Io(format!("failed reading RPC response: {error}")))?; if body.len() > MAX_RPC_RESPONSE_BYTES { return Err(response_too_large()); } if !status.is_success() { - return Err(DevkitError::Io(format!("RPC endpoint returned HTTP status {status}"))); + return Err(DevkitError::Io(format!( + "RPC endpoint returned HTTP status {status}" + ))); } let value: Value = serde_json::from_slice(&body).map_err(DevkitError::decode)?; if value.get("jsonrpc").and_then(Value::as_str) != Some("2.0") { - return Err(DevkitError::decode("RPC response is missing JSON-RPC version 2.0")); + return Err(DevkitError::decode( + "RPC response is missing JSON-RPC version 2.0", + )); } if value.get("id").and_then(Value::as_u64) != Some(id) { - return Err(DevkitError::decode("RPC response identifier does not match request")); + return Err(DevkitError::decode( + "RPC response identifier does not match request", + )); } if let Some(error) = value.get("error") { - let code = error.get("code").and_then(Value::as_i64).unwrap_or_default(); - let message = error.get("message").and_then(Value::as_str).unwrap_or("unknown RPC error"); + let code = error + .get("code") + .and_then(Value::as_i64) + .unwrap_or_default(); + let message = error + .get("message") + .and_then(Value::as_str) + .unwrap_or("unknown RPC error"); return Err(DevkitError::decode(format!("RPC error {code}: {message}"))); } - value.get("result").cloned() + value + .get("result") + .cloned() .ok_or_else(|| DevkitError::decode("RPC response has neither result nor error")) } } From 039e055a0fc3f2118eb891fd572025cfb2f10e90 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:20:52 +0100 Subject: [PATCH 21/57] style: finish formatting bounded WASM file reader --- crates/devkit-cli/src/main.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index be30e58..efa105c 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -295,7 +295,14 @@ fn read_wasm(path: &str) -> Result, CliError> { ) })?; if bytes.len() > devkit_contract::MAX_WASM_BYTES { - return Err(CliError::new(EXIT_ERROR, "invalid_input", format!("WASM exceeds the {}-byte limit", devkit_contract::MAX_WASM_BYTES))); + return Err(CliError::new( + EXIT_ERROR, + "invalid_input", + format!( + "WASM exceeds the {}-byte limit", + devkit_contract::MAX_WASM_BYTES + ), + )); } Ok(bytes) } From 66a5f4de92311255f90b43b1b232c9f246adb977 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:14:33 +0100 Subject: [PATCH 22/57] docs: align Drips readiness with current implementation --- docs/DRIPS_READINESS.md | 140 ++++++++++++---------------------------- 1 file changed, 41 insertions(+), 99 deletions(-) diff --git a/docs/DRIPS_READINESS.md b/docs/DRIPS_READINESS.md index fb64e67..5d053c8 100644 --- a/docs/DRIPS_READINESS.md +++ b/docs/DRIPS_READINESS.md @@ -1,123 +1,65 @@ # Drips Readiness -This document prepares the repository for a possible Drips Wave application. It -contains **no guarantee** of approval, payout, contributor activity, or points. +This document records the repository's current state for a possible Drips Wave application. It does not guarantee approval, funding, payout, contributor activity, or points. ## Project summary -StellarFoundry DevKit is a developer infrastructure toolkit for Stellar and -Soroban. It composes with official crates (`stellar-xdr`, `stellar-strkey`, and -later `stellar-rpc-client`) instead of reimplementing them, and provides a -workflow layer: decoding and inspection with structured output, a typed RPC -protocol layer with deterministic mocks, fixtures, analysis, and a CLI. +StellarFoundry DevKit is a Rust developer toolkit for Stellar and Soroban. It provides bounded XDR decoding, endpoint validation, a typed RPC protocol layer, WASM metadata inspection, deterministic structural findings, SARIF serialization, configuration parsing, and a CLI. -## Problem solved +## Problem and ecosystem fit -Stellar developers repeatedly need to decode XDR/SCVal, inspect transactions and -events, talk to RPC, and test those flows deterministically. That glue code is -rebuilt per project. DevKit centralizes it with stable, testable interfaces and -no AI dependency. +Stellar developers routinely need to inspect XDR values, transactions, events, contract artifacts, and RPC behavior. DevKit aims to consolidate these workflows behind testable Rust APIs and command-line interfaces while composing with ecosystem crates such as `stellar-xdr` and `stellar-strkey`. -## Why it belongs in the Stellar ecosystem +The project complements official Stellar tooling; it should not claim to replace the official CLI or RPC client. -- It targets Stellar and Soroban specifically and depends on official crates. -- It does not duplicate `stellar-cli` or the official RPC client; it complements - them with workflow, testing, and analysis. -- It is useful to the same developers the Stellar ecosystem funds. +## Implementation status on the current feature branch -## Current implementation status +Implemented in the PR under review (not yet merged to `main`): -- **Working:** workspace with `devkit-core`, `devkit-xdr`, `devkit-rpc`, - `devkit-cli`; bounded base64 decoding of `ScVal`/`TransactionEnvelope`/ - `ContractEvent` to JSON; strkey classification; endpoint security validation; - typed RPC protocol layer with a deterministic mock transport; CLI with stable - exit codes. -- **Not implemented:** live RPC transport, contract inspection, security - analysis, SARIF, GitHub Action, configuration, logging, benchmarks. -- See [PROJECT_STATUS.md](PROJECT_STATUS.md). +- Bounded base64 decoding for `ScVal`, `TransactionEnvelope`, and `ContractEvent`, with structured errors and malformed-input regression fixtures. +- RPC endpoint policy validation and a typed RPC client with deterministic mock transport. +- HTTP JSON-RPC transport with timeout, redirects disabled, a response-size cap, response validation, and structured errors. +- Non-executing WASM validation and structural metadata for imports, exports, and custom sections. +- Deterministic structural analysis findings with stable rule IDs and SARIF 2.1.0 serialization. +- Strict TOML configuration parsing, timeout validation, environment overrides, and static diagnostic events. +- CLI commands for WASM inspection, WASM analysis, and explicit RPC health checks. +- CI for formatting, Clippy, and Linux/Windows/macOS tests; a RustSec dependency-audit workflow; release-validation workflow and release checklist. -## Contributor workflow - -- Issues are labeled by `area/*`, `type/*`, `difficulty/*`, and `phase/*`. -- `good first issue` marks small, well-scoped tasks. -- Each issue states problem, why, context, scope, out-of-scope, guidance, - acceptance criteria, required tests, security considerations, documentation - requirements, dependencies, and a definition of done. -- Contributors request assignment before starting; PRs use `Closes #` and - run the local gate (fmt, clippy, tests). - -## Issue categories - -Core, RPC, XDR, SCVal, transactions, events, contract inspection, testing, -security, CLI, configuration, GitHub, SARIF, observability, documentation, -release. See [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md). - -## Issue quality standards - -- No fabricated or duplicate issues; titles are unique and each is traceable to - real functionality or maintenance work. -- Complexity is honest (`trivial`/`medium`/`high`); points are assigned in the - Drips dashboard. - -## Testing standards - -- Unit tests for pure functions; malformed-input tests for every decoder; - fixture-based tests; property tests and fuzzing planned. -- The local gate is `cargo fmt --check`, `cargo clippy -D warnings`, - `cargo test --all`. +See [IMPLEMENTED_CAPABILITIES.md](IMPLEMENTED_CAPABILITIES.md), [PROJECT_STATUS.md](PROJECT_STATUS.md), and the open pull requests for exact scope. -## Documentation standards +## Known limitations (must be represented honestly) -Every public feature is documented; `CHANGELOG.md` and `PROJECT_STATUS.md` are -kept factual. +- The `contractspecv0` section is detected and measured; individual XDR entries are not yet decoded into typed function, struct, and enum metadata. +- Analysis rules are structural heuristics, not a complete vulnerability detector and not proof of malicious intent. +- SARIF serialization exists, but automatic upload to GitHub code scanning is not configured. +- The configuration library parses TOML and environment overrides, but it is not yet fully wired into all CLI commands; `rpc-health` currently accepts an explicit endpoint argument. +- The feature and hardening PR branches have passed their recorded CI runs individually, but a combined integration run is required after reconciling their divergent bases. +- No claim is made here that the project has published crates or release binaries, has a large user base, or is approved for a Wave. -## Maintainer responsiveness plan - -- Initial response to new issues and PRs within 48 hours during an active Wave. -- Review decision within 72 hours of a reviewable PR. -- Assign contributors promptly; unresolved issues roll over. - -## Security process - -See [SECURITY.md](../SECURITY.md). No network access by default; untrusted input -is bounded and decoded with structured errors; every security claim is backed by -implementation and tests. - -## Release process - -See [ROADMAP.md](ROADMAP.md) and the release issue. Semantic versioning, -changelog, and (planned) cross-platform binaries. - -## Links +## Contributor workflow -- Repository: -- Backlog: [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md) -- Architecture: [ARCHITECTURE.md](ARCHITECTURE.md) -- Ecosystem research: [ECOSYSTEM_RESEARCH.md](ECOSYSTEM_RESEARCH.md) +- Issues are labeled by `area/*`, `type/*`, `difficulty/*`, and `phase/*`. +- Contributors request assignment before starting and follow each issue's scope and acceptance criteria. +- PRs reference the relevant issue, include tests and documentation, and report commands and results truthfully. +- The repository gate is `cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, and `cargo test --workspace --all-features`. CI additionally exercises Linux, Windows, and macOS; release validation checks packaging. -## Quality audit outcome +## Issue quality and backlog -Every open issue was audited against the Drips maintainer criteria. Results -(see [DRIPS_ISSUE_QUALITY_AUDIT.md](DRIPS_ISSUE_QUALITY_AUDIT.md)): +The backlog audit and issue labels are documented in [DRIPS_ISSUE_QUALITY_AUDIT.md](DRIPS_ISSUE_QUALITY_AUDIT.md) and [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md). Backlog issues are planned work, not completed features. Do not present issue count or labels as evidence that those features already exist. -- **89 Drips-ready** issues (`status/ready`). -- **24 blocked by dependency** (`status/blocked`) — not to be added to a Wave - until their dependency lands. -- **3 deferred / future work** (`status/deferred`). -- **3 needs revision** (`status/needs-revision`, clarified on the issue). -- **1 duplicate removed**, **1 merged** (issue count reduced from 121 to 119). +## Security and release process -Complexity labels were not altered. Drips determines applicable points and -budgets through its own system. +See [SECURITY.md](../SECURITY.md), [TESTING.md](TESTING.md), and [RELEASE_CHECKLIST.md](RELEASE_CHECKLIST.md). Security claims must correspond to implementation and tests; secrets, endpoint values, and payloads should not be written to logs. -## Known limitations +## Appeal readiness checklist -- The backlog contains 119 open issues. Most are **not implemented**; the - project is early (foundation and RPC protocol layer only). See - [BUILD_REPORT.md](BUILD_REPORT.md). -- No live RPC, no release binaries, no published crates. -- Traction is minimal and is reported honestly. +Before applying, confirm all of the following: -## Future roadmap +- [ ] Both pull requests have been reviewed against the current `main` base and their integration strategy is explicit. +- [ ] A combined change set has passed required CI after reconciliation, including Linux, Windows, macOS, formatting, and Clippy. +- [ ] The lockfile and dependency audit are consistent with the final manifest. +- [ ] User-facing documentation, project status, and changelog accurately reflect what is merged versus still proposed. +- [ ] The application describes concrete completed work, links to the actual PRs and tests, and distinguishes limitations from delivered functionality. +- [ ] A maintainer/program administrator has confirmed the applicable Wave application process and eligibility. -See [ROADMAP.md](ROADMAP.md). +Passing CI is necessary but does not by itself establish acceptance criteria, maintainer approval, or Drips eligibility. Do not submit an appeal until the integration and program-specific requirements are verified. From 8b862ff2a5e45a3086b8d5020924cae9cfbfdecd Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:14:43 +0100 Subject: [PATCH 23/57] docs: correct project status and integration gaps --- docs/PROJECT_STATUS.md | 97 +++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 59 deletions(-) diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index cffaf10..355aff2 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -1,75 +1,54 @@ # Project Status -**Current phase:** Phase 1 — Foundation (in progress) +**Current phase:** Foundation and developer-tooling implementation in review **Repository:** https://github.com/StellarFoundry/stellar-devkit **Default branch:** `main` -## Completed - -- Workspace: `devkit-core`, `devkit-xdr`, `devkit-cli`. -- `devkit-core`: structured errors, network profiles (public/testnet/futurenet/ - custom) with canonical passphrases, output format. -- `devkit-xdr`: bounded decoding of base64 `ScVal`, `TransactionEnvelope`, and - `ContractEvent` to JSON; strkey classification. -- `stellar-foundry` CLI: `version`, `doctor`, `strkey`, `scval`, `envelope`, - `event`, `endpoint`; terminal/JSON output; exit codes 0/2/3. -- `devkit-rpc`: typed RPC protocol layer with an injectable transport, a - deterministic mock, endpoint validation, typed health/network/latest-ledger - results, and a retry policy. **No live network transport yet.** -- CI: fmt, clippy, tests on Linux/Windows/macOS. -- Docs: ecosystem research, architecture, getting started, CLI, roadmap, this - file. -- Governance: README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, LICENSE, - CHANGELOG, PR/issue templates. - -## Not started (tracked as issues) - -- RPC layer (integration with `stellar-rpc-client`, mock transport, diagnostics). -- Configuration system and logging. -- Fixtures and a mock network for deterministic tests. -- Contract inspection (WASM metadata / spec). -- Transaction and event analysis (resources, fees, auth, affected state). -- Security analysis (malformed XDR, unsafe RPC configuration, SSRF policy). -- SARIF output and GitHub Action. -- Performance benchmarks and observability. -- Release engineering (binaries, crates.io, release automation). - -## Verified facts - -- `cargo build`, `cargo test`, `cargo clippy -D warnings`, `cargo fmt --check` - pass locally (run on 2026-09-22). Test count is reported in the build report. -- CLI smoke test: real contract strkey classified as `contract`. - -## Known limitations - -- JSON shapes come from `stellar-xdr`'s serde representation and are **unstable** - until a curated, versioned schema is defined. -- Decoding uses a permissive limit today; a stricter, configurable limit is - planned. -- No network functionality exists; `doctor` states this explicitly. -- No MSRV is pinned yet. -- `devkit-core` has no configuration loader yet. +**Status note:** The capabilities below include work on open pull-request branches. They are not all merged into `main`. See PRs [#127](https://github.com/StellarFoundry/stellar-devkit/pull/127) and [#133](https://github.com/StellarFoundry/stellar-devkit/pull/133). + +## Implemented in the current code / feature branch + +- `devkit-core`: structured errors, network profiles, output formats. +- `devkit-xdr`: bounded decoding of base64 `ScVal`, `TransactionEnvelope`, and `ContractEvent`; strkey classification; malformed-input regression fixtures. +- `stellar-foundry` CLI: `version`, `doctor`, `strkey`, `scval`, `envelope`, `event`, `endpoint`, `inspect-wasm`, `analyze-wasm`, and `rpc-health`; terminal/JSON output, SARIF output for analysis, and exit codes 0/2/3. +- `devkit-rpc`: typed RPC protocol layer, deterministic mock transport, endpoint validation, typed health/network/latest-ledger results, retry policy, and bounded HTTP JSON-RPC transport. +- `devkit-contract`: bounded, non-executing WASM validation and import/export/custom-section metadata. +- `devkit-analysis`: deterministic structural findings with stable rule IDs and SARIF 2.1.0 serialization. +- `devkit-config`: strict TOML parsing, timeout validation, environment overrides, and static diagnostics. +- CI: formatting, Clippy, and tests on Linux, Windows, and macOS; PR #127 also adds a RustSec dependency audit and declares Rust 1.91 as the workspace MSRV. +- Release preparation: release-validation workflow, release checklist, implementation-capabilities documentation, and updated Drips readiness checklist. + +## Verified CI status + +- PR #133 head `039e055a0fc3f2118eb891fd572025cfb2f10e90`: its recorded CI run passed formatting, Clippy, and Linux, Windows, and macOS tests. +- PR #127 head `140e3a547db4665c8195e83a070ea1c1c639e63e`: its recorded CI jobs and RustSec audit passed. +- These are separate branch runs. They do **not** establish that the combined changes pass after branch reconciliation. + +## Remaining limitations and integration work + +- `contractspecv0` is detected and sized but individual XDR specification entries are not yet decoded into typed function/struct/enum metadata. +- Security-analysis rules are structural heuristics, not a comprehensive vulnerability engine. +- SARIF serialization is implemented; automatic GitHub code-scanning upload is not configured. +- The configuration library exists, but it is not yet fully wired into every CLI command. `rpc-health` takes an explicit endpoint argument. +- The two open PR branches diverge from the current base; combined integration and CI remain required before treating the work as ready for a Drips Wave appeal. +- Do not describe unmerged work as already released or available from `main`. ## Security status -- No network access, no secrets, bounded decoding, structured errors. -- Fuzzing is planned, not yet implemented. +- XDR and WASM inputs are bounded in the implemented paths. +- The HTTP transport uses a finite timeout, disables redirects, bounds response bytes before parsing, and validates JSON-RPC responses. +- Endpoint policy rejects embedded credentials and permits plaintext HTTP only for loopback. +- Security claims should be limited to behavior demonstrated by code and tests; no system is claimed to be vulnerability-free. ## Contributor backlog -- **119 open issues** after a quality audit (down from 121). -- Audit result: **89 Drips-ready**, 24 blocked by dependency, 3 deferred, 3 needing - revision. See [DRIPS_ISSUE_QUALITY_AUDIT.md](DRIPS_ISSUE_QUALITY_AUDIT.md). -- Status labels `status/ready`, `status/blocked`, `status/deferred`, and - `status/needs-revision` are applied. -- Manifest: [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md); prior audit: - [DRIPS_ISSUE_AUDIT.md](DRIPS_ISSUE_AUDIT.md). -- The backlog is a roadmap: most issues are **not implemented**. +The issue backlog is a roadmap, not a list of completed features. Audit counts, labels, and issue descriptions must not be presented as proof that the corresponding work is implemented. See [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md) and [DRIPS_ISSUE_QUALITY_AUDIT.md](DRIPS_ISSUE_QUALITY_AUDIT.md). ## Drips documents -- [DRIPS_READINESS.md](DRIPS_READINESS.md) -- [DRIPS_APPLICATION_DRAFT.md](DRIPS_APPLICATION_DRAFT.md) -- [DRIPS_ISSUE_AUDIT.md](DRIPS_ISSUE_AUDIT.md) +- [Drips readiness checklist](DRIPS_READINESS.md) +- [Drips application draft](DRIPS_APPLICATION_DRAFT.md) +- [Issue audit](DRIPS_ISSUE_AUDIT.md) +- [Contributing through Drips Wave](WAVE.md) From 0b1c99e6a0c286ac16ac92ee6b3ff9a5395c75e4 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:14:51 +0100 Subject: [PATCH 24/57] docs: record current implementation and limitations --- CHANGELOG.md | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3178463..99c3c6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,30 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Workspace with `devkit-core`, `devkit-xdr`, and `devkit-cli`. -- `devkit-core`: `DevkitError`, `Network` profiles with canonical passphrases, - `OutputFormat`. -- `devkit-xdr`: decode base64 `ScVal`, `TransactionEnvelope`, and - `ContractEvent` to JSON with bounded reading; classify Stellar strkeys. -- `stellar-foundry` CLI: `version`, `doctor`, `strkey`, `scval`, `envelope`, - `event`, with terminal and JSON output and stable exit codes. -- `devkit-rpc`: typed Stellar RPC protocol layer with an injectable `Transport`, - a deterministic `MockTransport`, validated `Endpoint` (https only; loopback - `http`; no embedded credentials), typed `Health` / `NetworkInfo` / - `LatestLedger`, `getEvents` and `getFeeStats` passthrough, and a retry policy - that retries only transport failures. -- CLI: `endpoint ` validates an RPC endpoint against the security policy. -- Documentation: ecosystem research, architecture, getting started, CLI - reference, project status, roadmap. -- CI: format, clippy, and tests on Linux, Windows, and macOS. -- Tooling: `tools/changelog.py` generates reviewable release sections from - Conventional Commits; a dry-run workflow runs on demand and on release. -- Benchmark: `cargo run --release -p devkit-xdr --example mem_profile` reports - peak and current memory per decode input size (see `docs/MEMORY.md`). -- Fuzzing: `fuzz/` cargo-fuzz targets for XDR/SCVal decoding, exercised by a - weekly bounded scheduled workflow (see `docs/TESTING.md`). -- Governance: README, CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, LICENSE, - pull-request and issue templates. +- `devkit-contract`: bounded, non-executing WASM validation and structural + metadata for imports, exports, and custom sections. +- `devkit-analysis`: deterministic structural findings with stable rule IDs + and SARIF 2.1.0 serialization. +- `devkit-config`: strict TOML configuration parsing, timeout validation, + environment overrides, and static diagnostic events. +- `devkit-rpc`: bounded HTTP JSON-RPC transport with timeout, redirects + disabled, response-size limits, response validation, and structured errors. +- CLI commands `inspect-wasm`, `analyze-wasm`, and `rpc-health`. +- Release-validation workflow, release checklist, and Drips readiness checklist. +- RustSec dependency auditing and declared Rust 1.91 workspace MSRV in PR #127. +- Malformed-XDR regression corpus and bounded-input hardening. +- CI: formatting, Clippy, and Linux, Windows, and macOS tests. ### Fixed @@ -40,3 +29,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `Limits::none()`. A crafted XDR length prefix can no longer request an unbounded allocation (memory-exhaustion denial of service); such inputs are rejected as a structured decode error. Found by the `decode_scval` fuzz target. + +### Limitations + +- These changes are on open pull-request branches and are not all merged to + `main`. +- WASM contract specification entries are detected and sized, but individual + `contractspecv0` XDR entries are not yet decoded into typed metadata. +- Analysis findings are structural heuristics, not a comprehensive + vulnerability detector. +- SARIF serialization is available; automated GitHub code-scanning upload is + not configured. +- The configuration API is not yet wired into every CLI command. From 6dbbf42ae5834281aa28075f84bc3db952186c48 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:15:44 +0100 Subject: [PATCH 25/57] build: add temporary lockfile refresh workflow --- .github/workflows/refresh-lockfile.yml | 43 ++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/refresh-lockfile.yml diff --git a/.github/workflows/refresh-lockfile.yml b/.github/workflows/refresh-lockfile.yml new file mode 100644 index 0000000..91a049e --- /dev/null +++ b/.github/workflows/refresh-lockfile.yml @@ -0,0 +1,43 @@ +name: Refresh Cargo lockfile + +on: + push: + branches: + - feat/complete-devkit-workstreams + paths: + - "Cargo.toml" + - "**/Cargo.toml" + - ".github/workflows/refresh-lockfile.yml" + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: refresh-lockfile-${{ github.ref }} + cancel-in-progress: false + +jobs: + refresh: + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: true + - name: Install stable Rust + uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + - name: Regenerate lockfile + run: cargo generate-lockfile + - name: Commit lockfile if changed + shell: bash + run: | + if git diff --quiet -- Cargo.lock; then + echo "Cargo.lock is already current." + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add Cargo.lock + git commit -m "build: regenerate Cargo.lock" + git push From 0ff9d975c0748d2e58afd2714dcc271fd678dc51 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Oct 2026 11:16:02 +0000 Subject: [PATCH 26/57] build: regenerate Cargo.lock --- Cargo.lock | 965 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 864 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a11925..5a9858a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "android_system_properties" version = "0.1.6" @@ -47,7 +59,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -58,9 +70,15 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.61.2", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.1" @@ -85,6 +103,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + [[package]] name = "block-buffer" version = "0.10.4" @@ -115,11 +139,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + [[package]] name = "cc" -version = "1.4.7" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54413ede23c2daf518f35156dfde027feb2374004d63bd497f983c8db9c0e313" +checksum = "86e055fa6e3e40ad6187ba25b6855eeb635d5271f8e0f0e1e9a5826b8d75bea0" dependencies = [ "find-msvc-tools", "shlex", @@ -131,6 +161,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "cfg_eval" version = "0.1.2" @@ -142,6 +178,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core", +] + [[package]] name = "chrono" version = "0.4.45" @@ -185,7 +232,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -215,6 +262,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + [[package]] name = "crate-git-revision" version = "0.0.6" @@ -267,7 +323,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -278,7 +334,7 @@ checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ "darling_core", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -289,55 +345,84 @@ checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "defmt" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +checksum = "e4a0795ac740cb54d7b3775c9c17345f6aaecf884c01733b39d6017738e0e3e1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "defmt-macros", ] [[package]] name = "defmt-macros" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +checksum = "840bb056bd99c495c9188198a258ab787c79f306d864db3e6e8451442d6d8366" dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.9", ] [[package]] name = "defmt-parser" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +checksum = "71958f69fb2de856c5dd51ac9377e986ca1aa5919f931bf5395a807277f889a0" dependencies = [ - "thiserror 2.0.20", + "thiserror 2.0.21", ] [[package]] name = "deranged" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" dependencies = [ "serde_core", ] +[[package]] +name = "devkit-analysis" +version = "0.1.0" +dependencies = [ + "devkit-contract", + "serde", + "serde_json", +] + [[package]] name = "devkit-cli" version = "0.1.0" dependencies = [ "clap", + "devkit-analysis", + "devkit-config", + "devkit-contract", "devkit-core", "devkit-rpc", "devkit-xdr", "serde_json", ] +[[package]] +name = "devkit-config" +version = "0.1.0" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "devkit-contract" +version = "0.1.0" +dependencies = [ + "devkit-core", + "serde", + "wasmparser", +] + [[package]] name = "devkit-core" version = "0.1.0" @@ -353,6 +438,7 @@ name = "devkit-rpc" version = "0.1.0" dependencies = [ "devkit-core", + "reqwest", "serde", "serde_json", "url", @@ -387,7 +473,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -398,9 +484,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" [[package]] name = "escape-bytes" @@ -416,9 +502,9 @@ checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" [[package]] name = "find-msvc-tools" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25905e51abafe4dcea6c15fec58c57b601cdbd0ee53d22ea1d3016c587d39b" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" [[package]] name = "form_urlencoded" @@ -429,12 +515,34 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + [[package]] name = "futures-task" version = "0.3.34" @@ -448,7 +556,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -463,6 +574,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "rand_core", + "wasm-bindgen", +] + [[package]] name = "hash32" version = "0.3.1" @@ -478,6 +616,16 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "serde", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -510,6 +658,104 @@ dependencies = [ "serde", ] +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" +dependencies = [ + "atomic-waker", + "bytes", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -536,9 +782,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" dependencies = [ "displaydoc", "potential_utf", @@ -550,9 +796,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" dependencies = [ "displaydoc", "litemap", @@ -563,9 +809,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -577,15 +823,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" [[package]] name = "icu_properties" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" dependencies = [ "displaydoc", "icu_collections", @@ -598,15 +844,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" [[package]] name = "icu_provider" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" dependencies = [ "displaydoc", "icu_locale_core", @@ -667,6 +913,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -681,9 +933,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.37" +version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1baf72f08796de0260609515130699b890ac25f30e610ad894bc5856cafdb" +checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" dependencies = [ "defmt", "jiff-core", @@ -708,9 +960,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.37" +version = "0.2.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378268a1116ad67ae6228701118ac9f491d78fda38a40a1f1a9e1348de6f7212" +checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" dependencies = [ "jiff-core", "proc-macro2", @@ -720,9 +972,9 @@ dependencies = [ [[package]] name = "jiff-tzdb" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" +checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7" [[package]] name = "jiff-tzdb-platform" @@ -735,9 +987,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.105" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" dependencies = [ "cfg-if", "futures-util", @@ -746,9 +998,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.189" +version = "0.2.191" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" [[package]] name = "litemap" @@ -762,12 +1014,29 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + [[package]] name = "memchr" version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "num-conv" version = "0.2.2" @@ -833,9 +1102,9 @@ dependencies = [ [[package]] name = "powerfmt" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" [[package]] name = "proc-macro2" @@ -846,6 +1115,62 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.21", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.21", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.47" @@ -855,6 +1180,38 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core", +] + [[package]] name = "ref-cast" version = "1.0.27" @@ -872,7 +1229,102 @@ checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustls" +version = "0.23.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -881,6 +1333,12 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + [[package]] name = "schemars" version = "0.8.22" @@ -906,9 +1364,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" +checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e" dependencies = [ "dyn-clone", "ref-cast", @@ -916,6 +1374,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.229" @@ -943,14 +1407,14 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] name = "serde_json" -version = "1.0.151" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" dependencies = [ "itoa", "memchr", @@ -968,11 +1432,23 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_with" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935177bb8c0cd8ca1a4e6d1a2ac8988bea69cab4f9d3a31311e012ad27868ea4" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" dependencies = [ "base64 0.23.1", "bs58", @@ -983,7 +1459,7 @@ dependencies = [ "jiff", "schemars 0.8.22", "schemars 0.9.0", - "schemars 1.2.2", + "schemars 1.2.3", "serde_core", "serde_json", "serde_with_macros", @@ -992,14 +1468,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d607aa01a3cb0ad757d6fd216136910db3c97b102fe686585689615a02dbcdc" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" dependencies = [ "darling", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -1009,7 +1485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] @@ -1027,9 +1503,19 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.16.1" +version = "1.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" +checksum = "d0d5d4b1981b8bbbb7aed204cae5726d683d5d6ac7e74ec54122ea28f315e7c8" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] [[package]] name = "stable_deref_trait" @@ -1061,9 +1547,9 @@ dependencies = [ [[package]] name = "stellar-xdr" -version = "28.0.0" +version = "28.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d09ff8b9f919b084f664003c4c546ac66a76affd5429460dbe29f4b326f8e" +checksum = "52599dcc4daa661c19d912f5ad55322772515a198c0da3c985ef3922f7f40b77" dependencies = [ "base64 0.22.1", "cfg_eval", @@ -1083,6 +1569,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.119" @@ -1096,15 +1588,24 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.6" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.14.0" @@ -1113,7 +1614,7 @@ checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -1127,11 +1628,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ - "thiserror-impl 2.0.20", + "thiserror-impl 2.0.21", ] [[package]] @@ -1147,13 +1648,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] @@ -1188,9 +1689,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" dependencies = [ "displaydoc", "zerovec", @@ -1202,6 +1703,30 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" +[[package]] +name = "tokio" +version = "1.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "toml" version = "0.8.23" @@ -1243,6 +1768,76 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.20.1" @@ -1251,9 +1846,15 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "unicode-ident" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" @@ -1285,11 +1886,26 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "want" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasm-bindgen" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", @@ -1298,11 +1914,22 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + [[package]] name = "wasm-bindgen-macro" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1310,26 +1937,69 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasmparser" +version = "0.219.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5220ee4c6ffcc0cb9d7c47398052203bc902c8ef3985b0c8134118440c0b2921" +dependencies = [ + "ahash", + "bitflags 2.13.2", + "hashbrown 0.14.5", + "indexmap 2.14.2", + "semver", + "serde", +] + +[[package]] +name = "web-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -1389,6 +2059,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1398,6 +2077,70 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "0.7.15" @@ -1415,9 +2158,9 @@ checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "yoke" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -1426,16 +2169,36 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33811428bee40dbceb6d545e95754741d17a6aef9a4849f0fd62e2ba4f412a78" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "zerofrom" version = "0.1.8" @@ -1453,15 +2216,15 @@ checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", "synstructure", ] [[package]] name = "zeroize" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" dependencies = [ "zeroize_derive", ] @@ -1490,9 +2253,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" dependencies = [ "yoke", "zerofrom", @@ -1501,13 +2264,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.6", + "syn 3.0.9", ] [[package]] From 919011dcbeea1f1bd6f954b72e747e5b8ab368bd Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:16:39 +0100 Subject: [PATCH 27/57] chore: remove temporary lockfile refresh workflow --- .github/workflows/refresh-lockfile.yml | 43 -------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/refresh-lockfile.yml diff --git a/.github/workflows/refresh-lockfile.yml b/.github/workflows/refresh-lockfile.yml deleted file mode 100644 index 91a049e..0000000 --- a/.github/workflows/refresh-lockfile.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Refresh Cargo lockfile - -on: - push: - branches: - - feat/complete-devkit-workstreams - paths: - - "Cargo.toml" - - "**/Cargo.toml" - - ".github/workflows/refresh-lockfile.yml" - workflow_dispatch: - -permissions: - contents: write - -concurrency: - group: refresh-lockfile-${{ github.ref }} - cancel-in-progress: false - -jobs: - refresh: - runs-on: ubuntu-latest - steps: - - name: Checkout branch - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: true - - name: Install stable Rust - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable - - name: Regenerate lockfile - run: cargo generate-lockfile - - name: Commit lockfile if changed - shell: bash - run: | - if git diff --quiet -- Cargo.lock; then - echo "Cargo.lock is already current." - exit 0 - fi - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add Cargo.lock - git commit -m "build: regenerate Cargo.lock" - git push From 173a9468b4c7ad9ad44ee95c0211a800f3525352 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:21:01 +0100 Subject: [PATCH 28/57] ci: enforce locked builds and test declared MSRV --- .github/workflows/ci.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 712c8a7..d89ff6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: with: components: clippy - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 - - run: cargo clippy --all-targets --all-features -- -D warnings + - run: cargo clippy --locked --all-targets --all-features -- -D warnings test: name: Test (${{ matrix.os }}) @@ -50,4 +50,15 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 - - run: cargo test --all --all-features + - run: cargo test --locked --all --all-features + + msrv: + name: MSRV (Rust 1.91.0) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + with: + toolchain: 1.91.0 + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 + - run: cargo test --locked --all --all-features From 6052a8f4b8164c1936ccae7ca7cab394a87336ab Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:22:41 +0100 Subject: [PATCH 29/57] docs: update appeal readiness after branch reconciliation --- docs/DRIPS_READINESS.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/DRIPS_READINESS.md b/docs/DRIPS_READINESS.md index 5d053c8..df6b4dd 100644 --- a/docs/DRIPS_READINESS.md +++ b/docs/DRIPS_READINESS.md @@ -12,9 +12,9 @@ Stellar developers routinely need to inspect XDR values, transactions, events, c The project complements official Stellar tooling; it should not claim to replace the official CLI or RPC client. -## Implementation status on the current feature branch +## Implementation status on the current combined PR branch -Implemented in the PR under review (not yet merged to `main`): +The combined branch for PR #133 includes the feature work and the security-hardening changes from PR #127. This combined work is still under review and has **not** been merged to `main`. - Bounded base64 decoding for `ScVal`, `TransactionEnvelope`, and `ContractEvent`, with structured errors and malformed-input regression fixtures. - RPC endpoint policy validation and a typed RPC client with deterministic mock transport. @@ -23,9 +23,9 @@ Implemented in the PR under review (not yet merged to `main`): - Deterministic structural analysis findings with stable rule IDs and SARIF 2.1.0 serialization. - Strict TOML configuration parsing, timeout validation, environment overrides, and static diagnostic events. - CLI commands for WASM inspection, WASM analysis, and explicit RPC health checks. -- CI for formatting, Clippy, and Linux/Windows/macOS tests; a RustSec dependency-audit workflow; release-validation workflow and release checklist. +- Pinned CI actions, locked dependency resolution, a dedicated Rust 1.91.0 MSRV job, RustSec dependency auditing, release-validation workflow, and release checklist. -See [IMPLEMENTED_CAPABILITIES.md](IMPLEMENTED_CAPABILITIES.md), [PROJECT_STATUS.md](PROJECT_STATUS.md), and the open pull requests for exact scope. +See [IMPLEMENTED_CAPABILITIES.md](IMPLEMENTED_CAPABILITIES.md), [PROJECT_STATUS.md](PROJECT_STATUS.md), [CHANGELOG.md](../CHANGELOG.md), and [PR #133](https://github.com/StellarFoundry/stellar-devkit/pull/133) for exact scope. ## Known limitations (must be represented honestly) @@ -33,7 +33,6 @@ See [IMPLEMENTED_CAPABILITIES.md](IMPLEMENTED_CAPABILITIES.md), [PROJECT_STATUS. - Analysis rules are structural heuristics, not a complete vulnerability detector and not proof of malicious intent. - SARIF serialization exists, but automatic upload to GitHub code scanning is not configured. - The configuration library parses TOML and environment overrides, but it is not yet fully wired into all CLI commands; `rpc-health` currently accepts an explicit endpoint argument. -- The feature and hardening PR branches have passed their recorded CI runs individually, but a combined integration run is required after reconciling their divergent bases. - No claim is made here that the project has published crates or release binaries, has a large user base, or is approved for a Wave. ## Contributor workflow @@ -41,7 +40,7 @@ See [IMPLEMENTED_CAPABILITIES.md](IMPLEMENTED_CAPABILITIES.md), [PROJECT_STATUS. - Issues are labeled by `area/*`, `type/*`, `difficulty/*`, and `phase/*`. - Contributors request assignment before starting and follow each issue's scope and acceptance criteria. - PRs reference the relevant issue, include tests and documentation, and report commands and results truthfully. -- The repository gate is `cargo fmt --all -- --check`, `cargo clippy --workspace --all-targets --all-features -- -D warnings`, and `cargo test --workspace --all-features`. CI additionally exercises Linux, Windows, and macOS; release validation checks packaging. +- The repository gate is `cargo fmt --all -- --check`, `cargo clippy --locked --workspace --all-targets --all-features -- -D warnings`, and `cargo test --locked --workspace --all-features`. CI runs on Linux, Windows, and macOS and includes Rust 1.91.0 MSRV tests; release validation checks packaging. ## Issue quality and backlog @@ -53,13 +52,11 @@ See [SECURITY.md](../SECURITY.md), [TESTING.md](TESTING.md), and [RELEASE_CHECKL ## Appeal readiness checklist -Before applying, confirm all of the following: - -- [ ] Both pull requests have been reviewed against the current `main` base and their integration strategy is explicit. -- [ ] A combined change set has passed required CI after reconciliation, including Linux, Windows, macOS, formatting, and Clippy. -- [ ] The lockfile and dependency audit are consistent with the final manifest. -- [ ] User-facing documentation, project status, and changelog accurately reflect what is merged versus still proposed. -- [ ] The application describes concrete completed work, links to the actual PRs and tests, and distinguishes limitations from delivered functionality. +- [x] Both PR heads are included in a combined review branch; PR #127 remains open and unmerged. +- [x] The combined implementation passed CI on Linux, Windows, and macOS, plus formatting, Clippy, and Rust 1.91.0 MSRV tests. +- [ ] The RustSec audit has passed against the regenerated lockfile on the final combined head. +- [x] User-facing documentation, project status, and changelog distinguish proposed work from work merged to `main`. +- [ ] The application is finalized with links to the combined PR, CI results, and concrete completed work. - [ ] A maintainer/program administrator has confirmed the applicable Wave application process and eligibility. -Passing CI is necessary but does not by itself establish acceptance criteria, maintainer approval, or Drips eligibility. Do not submit an appeal until the integration and program-specific requirements are verified. +Passing CI is necessary but does not by itself establish maintainer approval or Drips eligibility. Do not submit an appeal until the final dependency audit and program-specific requirements are verified. From 0b7e977aa7ff02cd80be383e05768532205d1099 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:22:43 +0100 Subject: [PATCH 30/57] docs: record combined CI and outstanding audit --- docs/PROJECT_STATUS.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index 7e95f6e..77121bd 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -6,9 +6,9 @@ **Default branch:** `main` -**Status note:** The capabilities below include work on open pull-request branches. They are not all merged into `main`. See PRs [#127](https://github.com/StellarFoundry/stellar-devkit/pull/127) and [#133](https://github.com/StellarFoundry/stellar-devkit/pull/133). +**Status note:** The capabilities below are on an open pull-request branch and are not all merged into `main`. PR #133 includes PR #127's security-hardening head as a second parent for combined validation. Both PRs remain open; PR #127 is unmerged. -## Implemented in the current code / feature branch +## Implemented in the combined feature branch - `devkit-core`: structured errors, network profiles, output formats. - `devkit-xdr`: bounded decoding of base64 `ScVal`, `TransactionEnvelope`, and `ContractEvent`; strkey classification; malformed-input regression fixtures. @@ -17,16 +17,15 @@ - `devkit-contract`: bounded, non-executing WASM validation and import/export/custom-section metadata. - `devkit-analysis`: deterministic structural findings with stable rule IDs and SARIF 2.1.0 serialization. - `devkit-config`: strict TOML parsing, timeout validation, environment overrides, and static diagnostics. -- CI: formatting, Clippy, and tests on Linux, Windows, and macOS; Rust 1.91 MSRV job, scheduled fuzz smoke tests, and RustSec dependency audit. -- Release preparation: release-validation workflow, release checklist, implementation-capabilities documentation, and updated Drips readiness checklist. +- CI: pinned actions, locked dependency resolution, formatting, Clippy, Linux/Windows/macOS tests, and Rust 1.91.0 MSRV tests. +- Security: RustSec dependency audit workflow and bounded malformed-XDR regression corpus. +- Release preparation: release-validation workflow, release checklist, implementation-capabilities documentation, updated changelog, and Drips readiness checklist. ## Verified CI status -- The pre-integration PR #133 head passed formatting, Clippy, and Linux, Windows, and macOS tests. -- The pre-integration PR #127 head passed its recorded CI jobs and RustSec audit. -- The combined merge commit must pass a fresh CI run before the integration is considered validated. +The combined branch's latest CI run at `173a9468b4c7ad9ad44ee95c0211a800f3525352` passed all six jobs: formatting, Clippy, Linux tests, Windows tests, macOS tests, and Rust 1.91.0 MSRV tests. A fresh RustSec audit against the regenerated lockfile is still in progress. -## Remaining limitations and integration work +## Remaining limitations - `contractspecv0` is detected and sized but individual XDR specification entries are not yet decoded into typed function/struct/enum metadata. - Security-analysis rules are structural heuristics, not a comprehensive vulnerability engine. From 4da84f1776dbef41b562609a81a846cae2abb173 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:26:05 +0100 Subject: [PATCH 31/57] docs: refresh Drips application draft with verified scope --- docs/DRIPS_APPLICATION_DRAFT.md | 81 ++++++++++++++------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/docs/DRIPS_APPLICATION_DRAFT.md b/docs/DRIPS_APPLICATION_DRAFT.md index e3912f4..f01c354 100644 --- a/docs/DRIPS_APPLICATION_DRAFT.md +++ b/docs/DRIPS_APPLICATION_DRAFT.md @@ -1,67 +1,56 @@ # Drips Application Draft -Factual draft answers. Nothing here guarantees approval, points, or payout, and -no prior program participation, funding, community size, or contributor activity -is claimed. +This is a factual draft for review before any application is submitted. It does not guarantee acceptance, points, funding, or payout. Claims below distinguish the current open PR branch from features already merged to `main`. ## Project description -StellarFoundry DevKit is a developer infrastructure toolkit for Stellar and -Soroban. It provides bounded XDR/SCVal/transaction/event decoding with -structured output, a typed RPC protocol layer with a deterministic mock -transport, endpoint security validation, and a CLI (`stellar-foundry`). It -composes with official crates rather than reimplementing them, works fully -offline, and never requires AI. +StellarFoundry DevKit is a Rust developer toolkit for Stellar and Soroban. Its current implementation provides bounded decoding of `ScVal`, `TransactionEnvelope`, and `ContractEvent` XDR; Stellar strkey classification; RPC endpoint validation; a typed RPC protocol layer with deterministic mock transport; and a CLI named `stellar-foundry`. -## Stellar ecosystem relevance +The combined open implementation PR also adds bounded HTTP JSON-RPC transport, non-executing WASM structural inspection, deterministic structural findings, SARIF 2.1.0 serialization, strict TOML configuration APIs, and release/readiness workflows. These changes are still under review and should not be described as already merged or released. -The toolkit depends on official Stellar crates (`stellar-xdr`, -`stellar-strkey`) and targets Stellar/Soroban workflows directly. It complements -`stellar-cli` and the official RPC client instead of duplicating them, and it -serves developers building on Stellar. +## Problem and ecosystem relevance -## Planned issues +Stellar developers often need to inspect XDR values, contract artifacts, events, and RPC behavior during development and testing. DevKit centralizes several of these workflows behind reusable Rust APIs and a CLI, with an emphasis on bounded handling of untrusted inputs, deterministic tests, and structured output. -119 issues across core, RPC, XDR/SCVal, transactions, events, contract -inspection, testing, security, CLI, configuration, GitHub/SARIF, performance, -observability, release, and documentation. Complexity: 7 trivial, 84 medium, -28 high. See [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md) and -[DRIPS_ISSUE_AUDIT.md](DRIPS_ISSUE_AUDIT.md). Most issues are not yet -implemented; the working foundation is described in -[PROJECT_STATUS.md](PROJECT_STATUS.md). +The project composes with ecosystem crates such as `stellar-xdr` and `stellar-strkey`. It is intended to complement official Stellar tooling, not replace the official CLI or RPC client. -## Contributor opportunities +## Current implementation and evidence + +- Repository: [StellarFoundry/stellar-devkit](https://github.com/StellarFoundry/stellar-devkit) +- Combined implementation and hardening PR: [PR #133](https://github.com/StellarFoundry/stellar-devkit/pull/133) +- Security/CI hardening PR: [PR #127](https://github.com/StellarFoundry/stellar-devkit/pull/127) +- Latest combined CI run: [CI run #40679489331](https://github.com/StellarFoundry/stellar-devkit/actions/runs/40679489331) -Small, well-scoped `good first issue` tasks (completions, exit-code tests, -Dependabot) through medium features (typed RPC models, config system) to high -work (live transport, fuzzing, SSRF hardening, SARIF, release automation). +The latest combined CI run passed formatting, Clippy, Linux, Windows, macOS, and Rust 1.91.0 MSRV tests. The regenerated lockfile includes the new transport and WASM-parser dependencies. The RustSec audit is being rerun against the combined branch; confirm its final result before submitting an application. -## Supporting links +## Current limitations -- Repository: -- Architecture: [ARCHITECTURE.md](ARCHITECTURE.md) -- Ecosystem research: [ECOSYSTEM_RESEARCH.md](ECOSYSTEM_RESEARCH.md) +- The `contractspecv0` custom section is detected and measured, but individual XDR entries are not yet decoded into typed function, struct, and enum metadata. +- Security-analysis rules are structural heuristics, not a comprehensive vulnerability detector. +- SARIF serialization is implemented, but automatic GitHub code-scanning upload is not configured. +- Configuration parsing and environment overrides exist as a library API, but are not yet wired into every CLI command. +- The implementation PRs remain open. Do not state that all proposed features are already available on `main`. -## Current project status +## Contributor opportunities -Early. Working foundation plus RPC protocol layer with CI on Linux/Windows/macOS. -See [PROJECT_STATUS.md](PROJECT_STATUS.md). Live RPC and higher-level analysis are -not implemented. +The issue backlog documents potential work across RPC, XDR/SCVal, contract inspection, testing, security, CLI, configuration, SARIF, performance, observability, release engineering, and documentation. Backlog issues are proposed work, not completed functionality. Check current issue states and assignment requirements before describing specific issues as available. -## Maintainer experience +See [ISSUE_BACKLOG.md](ISSUE_BACKLOG.md), [DRIPS_ISSUE_AUDIT.md](DRIPS_ISSUE_AUDIT.md), and [DRIPS_READINESS.md](DRIPS_READINESS.md). -Maintained under the StellarFoundry organization by the account `Ayinkx01`. -StellarFoundry is not claimed to be independent of its maintainer; it is -maintained by `Ayinkx01`. +## Supporting documentation -## Technical roadmap +- [Architecture](ARCHITECTURE.md) +- [Ecosystem research](ECOSYSTEM_RESEARCH.md) +- [Project status](PROJECT_STATUS.md) +- [Testing guide](TESTING.md) +- [Release checklist](RELEASE_CHECKLIST.md) +- [Contributing through Drips Wave](WAVE.md) -Twelve phases from foundation through release; see [ROADMAP.md](ROADMAP.md). +## Final verification before submission -## Honesty statement +- Confirm the latest combined CI remains green after final documentation updates. +- Confirm the RustSec audit passes against the committed lockfile. +- Verify the current Drips Wave program's application process, eligibility, required evidence, and deadlines with the program maintainers. +- Replace this draft only with verified facts from the final repository state; do not claim approval, funding, user traction, or contributor activity without evidence. -- No approval, payout, or contributor activity is guaranteed. -- The issue set is smaller than 125; we do not inflate complexity to reach a - target. -- Metrics such as stars, contributors, and funding are not claimed unless - verified. +No application has been submitted by this draft. From f3d2bc8dace0e4d4c0e77871c931cfb82ae87ddd Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:27:00 +0100 Subject: [PATCH 32/57] docs: keep CI status wording current --- docs/PROJECT_STATUS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index 77121bd..b72104c 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -23,7 +23,7 @@ ## Verified CI status -The combined branch's latest CI run at `173a9468b4c7ad9ad44ee95c0211a800f3525352` passed all six jobs: formatting, Clippy, Linux tests, Windows tests, macOS tests, and Rust 1.91.0 MSRV tests. A fresh RustSec audit against the regenerated lockfile is still in progress. +The integrated code branch has passed all six CI jobs: formatting, Clippy, Linux tests, Windows tests, macOS tests, and Rust 1.91.0 MSRV tests. A later documentation-only update is being revalidated by CI. The RustSec audit against the regenerated lockfile is still in progress. ## Remaining limitations From 18f038e2850ade2813d45fe806eb99cc0c42b15b Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:33:08 +0100 Subject: [PATCH 33/57] docs: finalize Drips readiness evidence --- docs/DRIPS_APPLICATION_DRAFT.md | 2 +- docs/DRIPS_READINESS.md | 4 ++-- docs/PROJECT_STATUS.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/DRIPS_APPLICATION_DRAFT.md b/docs/DRIPS_APPLICATION_DRAFT.md index f01c354..ed70cac 100644 --- a/docs/DRIPS_APPLICATION_DRAFT.md +++ b/docs/DRIPS_APPLICATION_DRAFT.md @@ -21,7 +21,7 @@ The project composes with ecosystem crates such as `stellar-xdr` and `stellar-st - Security/CI hardening PR: [PR #127](https://github.com/StellarFoundry/stellar-devkit/pull/127) - Latest combined CI run: [CI run #40679489331](https://github.com/StellarFoundry/stellar-devkit/actions/runs/40679489331) -The latest combined CI run passed formatting, Clippy, Linux, Windows, macOS, and Rust 1.91.0 MSRV tests. The regenerated lockfile includes the new transport and WASM-parser dependencies. The RustSec audit is being rerun against the combined branch; confirm its final result before submitting an application. +The combined CI run passed formatting, Clippy, Linux, Windows, macOS, and Rust 1.91.0 MSRV tests: [CI run #40680053061](https://github.com/StellarFoundry/stellar-devkit/actions/runs/40680053061). The regenerated lockfile includes the new transport and WASM-parser dependencies, and the RustSec audit passed: [audit run #40680053077](https://github.com/StellarFoundry/stellar-devkit/actions/runs/40680053077). ## Current limitations diff --git a/docs/DRIPS_READINESS.md b/docs/DRIPS_READINESS.md index df6b4dd..83b72dc 100644 --- a/docs/DRIPS_READINESS.md +++ b/docs/DRIPS_READINESS.md @@ -54,9 +54,9 @@ See [SECURITY.md](../SECURITY.md), [TESTING.md](TESTING.md), and [RELEASE_CHECKL - [x] Both PR heads are included in a combined review branch; PR #127 remains open and unmerged. - [x] The combined implementation passed CI on Linux, Windows, and macOS, plus formatting, Clippy, and Rust 1.91.0 MSRV tests. -- [ ] The RustSec audit has passed against the regenerated lockfile on the final combined head. +- [x] The RustSec audit passed against the regenerated lockfile on the combined head. - [x] User-facing documentation, project status, and changelog distinguish proposed work from work merged to `main`. - [ ] The application is finalized with links to the combined PR, CI results, and concrete completed work. - [ ] A maintainer/program administrator has confirmed the applicable Wave application process and eligibility. -Passing CI is necessary but does not by itself establish maintainer approval or Drips eligibility. Do not submit an appeal until the final dependency audit and program-specific requirements are verified. +The combined CI and RustSec audit are green. Passing checks do not establish maintainer approval or Drips eligibility; verify the program-specific requirements before submitting. diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index b72104c..50d2597 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -23,7 +23,7 @@ ## Verified CI status -The integrated code branch has passed all six CI jobs: formatting, Clippy, Linux tests, Windows tests, macOS tests, and Rust 1.91.0 MSRV tests. A later documentation-only update is being revalidated by CI. The RustSec audit against the regenerated lockfile is still in progress. +The final combined branch at `f3d2bc8dace0e4d4c0e77871c931cfb82ae87ddd` passed all six CI jobs: formatting, Clippy, Linux tests, Windows tests, macOS tests, and Rust 1.91.0 MSRV tests. The RustSec audit also completed successfully against the regenerated lockfile; see [audit run #40680053077](https://github.com/StellarFoundry/stellar-devkit/actions/runs/40680053077). ## Remaining limitations From 840d6a5278bb9df26d35f1914c08cd38945615a1 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:41:19 +0100 Subject: [PATCH 34/57] feat(cli): wire RPC config and timeout settings --- crates/devkit-cli/src/main.rs | 68 +++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index efa105c..ed79fe8 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -9,6 +9,7 @@ use std::{ fs, io::{self, IsTerminal, Read}, process::ExitCode, + time::Duration, }; use clap::{Parser, Subcommand, ValueEnum}; @@ -40,6 +41,10 @@ impl From for OutputFormat { #[derive(Debug, Parser)] #[command(name = "stellar-foundry", version, about, long_about = None)] struct Cli { + /// Optional TOML configuration file (used by commands that support configuration). + #[arg(long, global = true)] + config: Option, + /// Output format. #[arg(long, value_enum, default_value_t = FormatArg::Terminal, global = true)] format: FormatArg, @@ -84,7 +89,10 @@ enum Command { /// Run structural checks against a WASM module; --format sarif emits SARIF 2.1.0. AnalyzeWasm { path: String }, /// Query getHealth from an explicitly supplied Stellar RPC endpoint. - RpcHealth { endpoint: String }, + RpcHealth { + /// RPC endpoint; overrides config file and environment. + endpoint: Option, + }, } #[derive(Debug)] @@ -165,6 +173,7 @@ fn print_json_error(category: &str, message: &str) { fn run(cli: Cli) -> Result { let format: OutputFormat = cli.format.into(); + let config_path = cli.config.clone(); match cli.command { Command::Version => { println!("{TOOL_NAME} {VERSION}"); @@ -223,9 +232,24 @@ fn run(cli: Cli) -> Result { Ok(EXIT_SUCCESS) } Command::RpcHealth { endpoint } => { + let mut config = load_config(config_path.as_deref())?; + config.apply_environment().map_err(|message| { + CliError::new(EXIT_USAGE, "configuration", message) + })?; + let endpoint = endpoint.or(config.rpc_url).ok_or_else(|| { + CliError::new( + EXIT_USAGE, + "configuration", + "provide an RPC endpoint argument, set rpc_url in the config file, or set STELLAR_FOUNDRY_RPC_URL", + ) + })?; + devkit_config::diagnostic(config.verbose, "rpc_health_start"); let endpoint = devkit_rpc::Endpoint::parse(&endpoint).map_err(runtime)?; - let transport = - devkit_rpc::HttpTransport::with_default_timeout(endpoint).map_err(runtime)?; + let transport = devkit_rpc::HttpTransport::new( + endpoint, + Duration::from_millis(config.timeout_ms), + ) + .map_err(runtime)?; let health = devkit_rpc::Client::new(transport) .health() .map_err(runtime)?; @@ -269,6 +293,44 @@ fn doctor(format: OutputFormat) { print_value(format, &report); } +/// Load optional TOML configuration with a strict size limit. Environment +/// overrides are applied by the command so unrelated CLI commands remain +/// independent of RPC-specific environment variables. +fn load_config(path: Option<&str>) -> Result { + const MAX_CONFIG_BYTES: u64 = 1024 * 1024; + let mut config = if let Some(path) = path { + let metadata = fs::metadata(path).map_err(|error| { + CliError::new(EXIT_ERROR, "io", format!("failed to inspect config file: {error}")) + })?; + if metadata.len() > MAX_CONFIG_BYTES { + return Err(CliError::new( + EXIT_USAGE, + "configuration", + "config file exceeds the 1048576-byte limit", + )); + } + let content = fs::read_to_string(path).map_err(|error| { + CliError::new(EXIT_ERROR, "io", format!("failed to read config file: {error}")) + })?; + if content.len() as u64 > MAX_CONFIG_BYTES { + return Err(CliError::new( + EXIT_USAGE, + "configuration", + "config file exceeds the 1048576-byte limit", + )); + } + devkit_config::Config::parse_toml(&content).map_err(|message| { + CliError::new(EXIT_USAGE, "configuration", message) + })? + } else { + devkit_config::Config::default() + }; + config.validate().map_err(|message| { + CliError::new(EXIT_USAGE, "configuration", message) + })?; + Ok(config) +} + fn read_wasm(path: &str) -> Result, CliError> { let metadata = fs::metadata(path).map_err(|error| { CliError::new( From 892f5750e7ccbd23c7fd71d437341f22ddd5a639 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:41:51 +0100 Subject: [PATCH 35/57] test(cli): cover RPC config loading --- crates/devkit-cli/src/main.rs | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index ed79fe8..1506d61 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -426,3 +426,39 @@ fn runtime(error: devkit_core::DevkitError) -> CliError { }; CliError::new(EXIT_ERROR, category, error.to_string()) } + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn loads_rpc_config_file() { + let path = std::env::temp_dir().join(format!( + "stellar-foundry-config-{}.toml", + std::process::id() + )); + fs::write( + &path, + "rpc_url = \"https://soroban-testnet.stellar.org\"\ntimeout_ms = 2500\nverbose = true\n", + ) + .expect("write temporary config"); + let config = load_config(path.to_str()).expect("valid config"); + let _ = fs::remove_file(&path); + assert_eq!(config.rpc_url.as_deref(), Some("https://soroban-testnet.stellar.org")); + assert_eq!(config.timeout_ms, 2500); + assert!(config.verbose); + } + + #[test] + fn rejects_unknown_config_keys() { + let path = std::env::temp_dir().join(format!( + "stellar-foundry-invalid-config-{}.toml", + std::process::id() + )); + fs::write(&path, "unexpected = true\n").expect("write temporary config"); + let result = load_config(path.to_str()); + let _ = fs::remove_file(&path); + assert!(result.is_err()); + } +} From 083aff4efd72a9d84c332a4ee7dd7442239404df Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:41:57 +0100 Subject: [PATCH 36/57] docs(cli): document RPC config and timeout overrides --- docs/CLI.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/CLI.md b/docs/CLI.md index df6aa73..4d7eca5 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -3,10 +3,10 @@ The `stellar-foundry` binary. ```text -stellar-foundry [--format terminal|json] +stellar-foundry [--config PATH] [--format terminal|json|sarif] ``` -`--format` is global and defaults to `terminal`. +`--format` is global and defaults to `terminal`. `--config PATH` is also global; the TOML configuration is currently consumed by `rpc-health`. ## Commands @@ -38,6 +38,27 @@ Decodes a base64 XDR `ContractEvent` into JSON. Pass `-` to read one value from When `-` is supplied, stdin must be piped or redirected; the CLI never waits for interactive terminal input. Input is limited to 16 MiB and must be valid UTF-8. Only one value is read; streaming multiple values is not supported. +### `rpc-health [ENDPOINT]` + +Checks the health endpoint of a Stellar RPC server. The endpoint can be supplied as an argument, configured as `rpc_url` in a TOML file, or supplied through `STELLAR_FOUNDRY_RPC_URL`. Precedence is command-line endpoint, then environment override, then config file. The request timeout defaults to 10 seconds and can be set with `timeout_ms` (100–120000) or `STELLAR_FOUNDRY_TIMEOUT_MS`. Set `verbose = true` or `STELLAR_FOUNDRY_VERBOSE=true` to emit static diagnostic event names; endpoint values and response payloads are not logged. + +Example config: + +```toml +rpc_url = "https://soroban-testnet.stellar.org" +timeout_ms = 10000 +verbose = false +``` + +Example invocation: + +```text +stellar-foundry --config ./stellar-foundry.toml rpc-health +stellar-foundry rpc-health https://soroban-testnet.stellar.org +``` + +Configuration files are limited to 1 MiB. Unknown TOML keys and invalid timeout values are rejected. + ## Exit codes | Code | Meaning | From a712999faceb4e0567a46622c8f94c4537560b55 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:42:04 +0100 Subject: [PATCH 37/57] docs: record CLI config integration --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99c3c6b..9db0580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `devkit-rpc`: bounded HTTP JSON-RPC transport with timeout, redirects disabled, response-size limits, response validation, and structured errors. - CLI commands `inspect-wasm`, `analyze-wasm`, and `rpc-health`. +- `rpc-health` now consumes bounded TOML configuration, timeout settings, and environment overrides; endpoint precedence is explicit in the CLI reference. - Release-validation workflow, release checklist, and Drips readiness checklist. - RustSec dependency auditing and declared Rust 1.91 workspace MSRV in PR #127. - Malformed-XDR regression corpus and bounded-input hardening. @@ -40,4 +41,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 vulnerability detector. - SARIF serialization is available; automated GitHub code-scanning upload is not configured. -- The configuration API is not yet wired into every CLI command. +- The configuration API is currently wired into `rpc-health`; other commands do not consume RPC configuration because they do not need it. From 3900ccc39a301bad9091a53dd341e8ed8fe93a62 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:42:38 +0100 Subject: [PATCH 38/57] feat(contract): use Stellar XDR for contract specs --- crates/devkit-contract/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/devkit-contract/Cargo.toml b/crates/devkit-contract/Cargo.toml index 98820a5..8dce73c 100644 --- a/crates/devkit-contract/Cargo.toml +++ b/crates/devkit-contract/Cargo.toml @@ -10,4 +10,6 @@ description = "Bounded, non-executing inspection of Soroban contract WASM artifa [dependencies] devkit-core.workspace = true serde.workspace = true +serde_json.workspace = true +stellar-xdr.workspace = true wasmparser.workspace = true From 3195c5890d1d58ddb6bde7a55ab3933575348085 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:42:41 +0100 Subject: [PATCH 39/57] feat(contract): decode bounded contractspecv0 XDR entries --- crates/devkit-contract/src/lib.rs | 79 ++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs index db342e0..173daa3 100644 --- a/crates/devkit-contract/src/lib.rs +++ b/crates/devkit-contract/src/lib.rs @@ -5,11 +5,17 @@ //! XDR specification entries is intentionally reported as a limitation until //! a versioned spec model is added. +use std::io::Cursor; + use devkit_core::DevkitError; use serde::Serialize; +use serde_json::Value; +use stellar_xdr::{Limited, Limits, ReadXdr, ScSpecEntry}; use wasmparser::{Parser, Payload, Validator}; pub const MAX_WASM_BYTES: usize = 16 * 1024 * 1024; +const MAX_SPEC_ENTRIES: usize = 4096; +const MAX_XDR_DEPTH: u32 = 128; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct ImportInfo { @@ -24,7 +30,17 @@ pub struct ExportInfo { pub index: u32, } -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct ContractSpecEntry { + /// Stable variant name from the official XDR model. + pub kind: String, + /// Best-effort declared name for functions and user-defined types. + pub name: Option, + /// Complete XDR-JSON representation of the decoded entry. + pub definition: Value, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] pub struct ContractReport { pub wasm_bytes: usize, pub imports: Vec, @@ -32,6 +48,60 @@ pub struct ContractReport { pub custom_sections: Vec, pub has_contract_spec: bool, pub contract_spec_bytes: usize, + pub contract_spec_entries: Vec, +} + +/// Decode the concatenated ScSpecEntry XDR stream with explicit resource +/// limits. The WASM module is already capped at 16 MiB; an additional entry +/// cap prevents a tiny malicious stream from producing an excessive report. +fn decode_contract_spec(bytes: &[u8]) -> Result, DevkitError> { + if bytes.is_empty() { + return Ok(Vec::new()); + } + let limits = Limits { + depth: MAX_XDR_DEPTH, + len: MAX_WASM_BYTES, + }; + let cursor = Cursor::new(bytes); + let mut reader = Limited::new(cursor, limits); + let mut entries = Vec::new(); + for item in ScSpecEntry::read_xdr_iter(&mut reader) { + let entry = item.map_err(|error| { + DevkitError::decode(format!("invalid contractspecv0 XDR entry: {error}")) + })?; + if entries.len() >= MAX_SPEC_ENTRIES { + return Err(DevkitError::invalid(format!( + "contractspecv0 exceeds the {MAX_SPEC_ENTRIES}-entry limit" + ))); + } + let definition = serde_json::to_value(&entry).map_err(DevkitError::decode)?; + let debug = format!("{entry:?}"); + let kind = debug + .split(['(', ' ', '{']) + .next() + .unwrap_or("Unknown") + .to_owned(); + let name = find_name(&definition); + entries.push(ContractSpecEntry { + kind, + name, + definition, + }); + } + Ok(entries) +} + +fn find_name(value: &Value) -> Option { + match value { + Value::Object(object) => { + if let Some(Value::String(name)) = object.get("name") { + return Some(name.clone()); + } + object.values().find_map(find_name) + } + Value::Array(values) => values.iter().find_map(find_name), + _ => None, + } } pub fn inspect_wasm(bytes: &[u8]) -> Result { @@ -53,6 +123,7 @@ pub fn inspect_wasm(bytes: &[u8]) -> Result { custom_sections: Vec::new(), has_contract_spec: false, contract_spec_bytes: 0, + contract_spec_entries: Vec::new(), }; for payload in Parser::new(0).parse_all(bytes) { match payload.map_err(DevkitError::decode)? { @@ -78,8 +149,14 @@ pub fn inspect_wasm(bytes: &[u8]) -> Result { Payload::CustomSection(section) => { let name = section.name().to_owned(); if name == "contractspecv0" { + if report.has_contract_spec { + return Err(DevkitError::decode( + "WASM module contains multiple contractspecv0 sections", + )); + } report.has_contract_spec = true; report.contract_spec_bytes = section.data().len(); + report.contract_spec_entries = decode_contract_spec(section.data())?; } report.custom_sections.push(name); } From eb5ba98cb2c513c485df819bacf02da0876efca1 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:43:00 +0100 Subject: [PATCH 40/57] ci(security): add manual SARIF code scanning upload --- .github/workflows/upload-sarif.yml | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/upload-sarif.yml diff --git a/.github/workflows/upload-sarif.yml b/.github/workflows/upload-sarif.yml new file mode 100644 index 0000000..4e841ad --- /dev/null +++ b/.github/workflows/upload-sarif.yml @@ -0,0 +1,41 @@ +name: Upload contract analysis to GitHub code scanning + +on: + workflow_dispatch: + inputs: + wasm_path: + description: Repository-relative path to a Soroban contract WASM artifact + required: true + type: string + +permissions: + contents: read + security-events: write + actions: read + +jobs: + analyze: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install stable Rust + uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + - name: Validate artifact path + env: + WASM_PATH: ${{ inputs.wasm_path }} + shell: bash + run: | + case "$WASM_PATH" in + ""|/*|..|../*|*/../*|*/..) echo "wasm_path must be a repository-relative path without parent traversal" >&2; exit 2 ;; + esac + test -f "$WASM_PATH" || { echo "WASM artifact not found in repository" >&2; exit 2; } + - name: Generate SARIF report + env: + WASM_PATH: ${{ inputs.wasm_path }} + run: cargo run --locked --bin stellar-foundry -- --format sarif analyze-wasm "$WASM_PATH" > results.sarif + - name: Upload SARIF + uses: github/codeql-action/upload-sarif@4bfa242aa49c5f7c6770b98670d354a7dfac05f8 # v3 + with: + sarif_file: results.sarif + category: stellar-foundry-contract-analysis From 8cd33bd259ae23f82c2b0c8894bdafbe086eef6f Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:43:16 +0100 Subject: [PATCH 41/57] docs: update contract spec and SARIF capabilities --- docs/IMPLEMENTED_CAPABILITIES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/IMPLEMENTED_CAPABILITIES.md b/docs/IMPLEMENTED_CAPABILITIES.md index fbf2e13..cf463af 100644 --- a/docs/IMPLEMENTED_CAPABILITIES.md +++ b/docs/IMPLEMENTED_CAPABILITIES.md @@ -3,9 +3,9 @@ This branch adds reusable library APIs for the remaining developer-tooling workstreams: - **RPC transport:** `devkit_rpc::HttpTransport` sends JSON-RPC POST requests with a finite timeout, disabled redirects, request ID validation, and a response byte cap enforced before JSON parsing. Endpoint policy continues to allow HTTPS and loopback-only HTTP. -- **WASM inspection:** `devkit_contract::inspect_wasm` validates the WASM container without executing it, and reports imports, exports, custom sections, and the size/presence of Soroban's `contractspecv0` section. It does not yet decode each XDR spec entry into typed functions/structs/enums. +- **WASM inspection:** `devkit_contract::inspect_wasm` validates the WASM container without executing it, and reports imports, exports, custom sections, and decodes the `contractspecv0` section as a bounded stream of official `ScSpecEntry` XDR values. It returns each decoded definition as XDR-JSON with a variant kind and best-effort declared name; the stream is capped at 4,096 entries. - **Analysis and SARIF:** `devkit_analysis::analyze` emits deterministic findings with stable rule IDs and evidence. `to_sarif` produces SARIF 2.1.0. Current rules are structural heuristics, not proof of a vulnerability. - **Configuration/diagnostics:** `devkit_config::Config` parses strict TOML, validates timeouts, and supports environment overrides. Verbose diagnostics accept static event names and avoid printing endpoints or payloads. - **Release readiness:** the release-validation workflow runs tests, formatting/lint checks, and package validation on version tags. -CLI commands are available as `inspect-wasm`, `analyze-wasm`, and `rpc-health`. GitHub code-scanning upload automation and decoding each XDR contract spec entry remain follow-up integration work. No WASM is executed and no network request is made unless a caller explicitly constructs and uses the HTTP transport. +CLI commands are available as `inspect-wasm`, `analyze-wasm`, and `rpc-health`. A manual GitHub Actions workflow can upload SARIF to code scanning when given a repository-relative WASM artifact path. The analysis rules remain structural heuristics, not a comprehensive vulnerability engine. No WASM is executed and no network request is made unless a caller explicitly constructs and uses the HTTP transport. From c70447ca3194b633e1ef07705da34c846867fcfd Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:43:19 +0100 Subject: [PATCH 42/57] docs: update integrated capability and validation status --- docs/PROJECT_STATUS.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index 50d2597..6ed6204 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -14,23 +14,23 @@ - `devkit-xdr`: bounded decoding of base64 `ScVal`, `TransactionEnvelope`, and `ContractEvent`; strkey classification; malformed-input regression fixtures. - `stellar-foundry` CLI: `version`, `doctor`, `strkey`, `scval`, `envelope`, `event`, `endpoint`, `inspect-wasm`, `analyze-wasm`, and `rpc-health`; terminal/JSON output, SARIF output for analysis, and exit codes 0/2/3. - `devkit-rpc`: typed RPC protocol layer, deterministic mock transport, endpoint validation, typed health/network/latest-ledger results, retry policy, and bounded HTTP JSON-RPC transport. -- `devkit-contract`: bounded, non-executing WASM validation and import/export/custom-section metadata. +- `devkit-contract`: bounded, non-executing WASM validation, import/export/custom-section metadata, and bounded `contractspecv0` XDR stream decoding into entry kind/name/definition records. - `devkit-analysis`: deterministic structural findings with stable rule IDs and SARIF 2.1.0 serialization. - `devkit-config`: strict TOML parsing, timeout validation, environment overrides, and static diagnostics. - CI: pinned actions, locked dependency resolution, formatting, Clippy, Linux/Windows/macOS tests, and Rust 1.91.0 MSRV tests. -- Security: RustSec dependency audit workflow and bounded malformed-XDR regression corpus. +- Security: RustSec dependency audit workflow, bounded malformed-XDR regression corpus, and a manual SARIF upload workflow for GitHub code scanning. - Release preparation: release-validation workflow, release checklist, implementation-capabilities documentation, updated changelog, and Drips readiness checklist. ## Verified CI status -The final combined branch at `f3d2bc8dace0e4d4c0e77871c931cfb82ae87ddd` passed all six CI jobs: formatting, Clippy, Linux tests, Windows tests, macOS tests, and Rust 1.91.0 MSRV tests. The RustSec audit also completed successfully against the regenerated lockfile; see [audit run #40680053077](https://github.com/StellarFoundry/stellar-devkit/actions/runs/40680053077). +The previously integrated branch passed formatting, Clippy, Linux, Windows, macOS, and Rust 1.91.0 MSRV tests, plus RustSec. New contract-spec decoding, CLI config wiring, and SARIF workflow changes are awaiting a fresh combined CI and audit run before merge. ## Remaining limitations -- `contractspecv0` is detected and sized but individual XDR specification entries are not yet decoded into typed function/struct/enum metadata. +- `contractspecv0` entries are decoded into XDR-JSON definitions with variant names and best-effort declared names; this is not yet a fully normalized ABI schema for every entry type. - Security-analysis rules are structural heuristics, not a comprehensive vulnerability engine. -- SARIF serialization is implemented; automatic GitHub code-scanning upload is not configured. -- The configuration library exists, but it is not yet fully wired into every CLI command. `rpc-health` takes an explicit endpoint argument. +- SARIF upload is available through a manually triggered workflow; it requires a repository-relative WASM artifact and GitHub code-scanning permissions. +- RPC configuration is wired into `rpc-health`: command-line endpoint overrides environment, which overrides the TOML file. Other commands do not consume RPC configuration because they do not need it. - Do not describe unmerged work as already released or available from `main`. ## Security status From b5d865a674c4ac2254f3d1d2d600f39418a5b2a0 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:43:22 +0100 Subject: [PATCH 43/57] docs: refresh Drips readiness for remaining work --- docs/DRIPS_READINESS.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/DRIPS_READINESS.md b/docs/DRIPS_READINESS.md index 83b72dc..640e709 100644 --- a/docs/DRIPS_READINESS.md +++ b/docs/DRIPS_READINESS.md @@ -19,20 +19,20 @@ The combined branch for PR #133 includes the feature work and the security-harde - Bounded base64 decoding for `ScVal`, `TransactionEnvelope`, and `ContractEvent`, with structured errors and malformed-input regression fixtures. - RPC endpoint policy validation and a typed RPC client with deterministic mock transport. - HTTP JSON-RPC transport with timeout, redirects disabled, a response-size cap, response validation, and structured errors. -- Non-executing WASM validation and structural metadata for imports, exports, and custom sections. +- Non-executing WASM validation, structural metadata, and bounded decoding of `contractspecv0` XDR entries into entry kind/name/definition records. - Deterministic structural analysis findings with stable rule IDs and SARIF 2.1.0 serialization. - Strict TOML configuration parsing, timeout validation, environment overrides, and static diagnostic events. -- CLI commands for WASM inspection, WASM analysis, and explicit RPC health checks. +- CLI commands for WASM inspection, WASM analysis, and RPC health checks using optional TOML config, environment overrides, and a validated timeout. - Pinned CI actions, locked dependency resolution, a dedicated Rust 1.91.0 MSRV job, RustSec dependency auditing, release-validation workflow, and release checklist. See [IMPLEMENTED_CAPABILITIES.md](IMPLEMENTED_CAPABILITIES.md), [PROJECT_STATUS.md](PROJECT_STATUS.md), [CHANGELOG.md](../CHANGELOG.md), and [PR #133](https://github.com/StellarFoundry/stellar-devkit/pull/133) for exact scope. ## Known limitations (must be represented honestly) -- The `contractspecv0` section is detected and measured; individual XDR entries are not yet decoded into typed function, struct, and enum metadata. +- Contract-spec entries are decoded into XDR-JSON definitions with a variant kind and best-effort name; a normalized, fully typed ABI schema is still future work. - Analysis rules are structural heuristics, not a complete vulnerability detector and not proof of malicious intent. -- SARIF serialization exists, but automatic upload to GitHub code scanning is not configured. -- The configuration library parses TOML and environment overrides, but it is not yet fully wired into all CLI commands; `rpc-health` currently accepts an explicit endpoint argument. +- SARIF upload is available through a manual workflow dispatch and requires a WASM artifact in the repository plus code-scanning permissions. +- RPC config is wired into `rpc-health`; other commands do not use RPC settings. Endpoint precedence is command-line, then environment, then config file. - No claim is made here that the project has published crates or release binaries, has a large user base, or is approved for a Wave. ## Contributor workflow @@ -56,7 +56,7 @@ See [SECURITY.md](../SECURITY.md), [TESTING.md](TESTING.md), and [RELEASE_CHECKL - [x] The combined implementation passed CI on Linux, Windows, and macOS, plus formatting, Clippy, and Rust 1.91.0 MSRV tests. - [x] The RustSec audit passed against the regenerated lockfile on the combined head. - [x] User-facing documentation, project status, and changelog distinguish proposed work from work merged to `main`. -- [ ] The application is finalized with links to the combined PR, CI results, and concrete completed work. +- [ ] The application is finalized with links to the combined PR, latest CI/audit results, and concrete completed work. - [ ] A maintainer/program administrator has confirmed the applicable Wave application process and eligibility. The combined CI and RustSec audit are green. Passing checks do not establish maintainer approval or Drips eligibility; verify the program-specific requirements before submitting. From dbaf9eb1e12aa70e4f362184a5e9fcf91e3447b2 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:43:38 +0100 Subject: [PATCH 44/57] test(contract): reject malformed contract spec XDR --- crates/devkit-contract/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs index 173daa3..f0f96c6 100644 --- a/crates/devkit-contract/src/lib.rs +++ b/crates/devkit-contract/src/lib.rs @@ -193,4 +193,17 @@ mod tests { assert!(report.exports.is_empty()); assert!(!report.has_contract_spec); } + #[test] + fn rejects_malformed_contract_spec_xdr() { + let name = b"contractspecv0"; + let payload_len = 1 + name.len() + 1; + let mut wasm = b"\\0asm\\x01\\0\\0\\0".to_vec(); + wasm.extend_from_slice(&[0, payload_len as u8, name.len() as u8]); + wasm.extend_from_slice(name); + wasm.push(0xff); + + let error = inspect_wasm(&wasm).unwrap_err().to_string(); + assert!(error.contains("contractspecv0 XDR entry"), "{error}"); + } + } From e4ce249d2d5fedf87b84aed994ddc81920cd1623 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:43:59 +0100 Subject: [PATCH 45/57] fix(analysis): initialize contract spec report field --- crates/devkit-analysis/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/devkit-analysis/src/lib.rs b/crates/devkit-analysis/src/lib.rs index 8647a04..f160e82 100644 --- a/crates/devkit-analysis/src/lib.rs +++ b/crates/devkit-analysis/src/lib.rs @@ -105,6 +105,7 @@ mod tests { custom_sections: vec![], has_contract_spec: false, contract_spec_bytes: 0, + contract_spec_entries: vec![], }; let findings = analyze(&report); assert!(findings From 9655ef451c2c61abe4ee9b8360d711b9e99b24d4 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:44:05 +0100 Subject: [PATCH 46/57] docs(contract): describe bounded XDR spec decoding From cdba7b0d75d0ef91e0dee2716be246f5dc0700a4 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:44:22 +0100 Subject: [PATCH 47/57] docs(contract): describe bounded XDR spec decoding From a37a9900595544fd504b12309a99b32c62738154 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:44:39 +0100 Subject: [PATCH 48/57] docs(contract): describe bounded XDR spec decoding --- crates/devkit-contract/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs index f0f96c6..03c4ee7 100644 --- a/crates/devkit-contract/src/lib.rs +++ b/crates/devkit-contract/src/lib.rs @@ -1,9 +1,8 @@ //! Bounded structural inspection for untrusted Soroban contract WASM. //! //! This crate validates the WASM container but never executes it. The -//! contractspecv0 custom section is detected and sized; decoding individual -//! XDR specification entries is intentionally reported as a limitation until -//! a versioned spec model is added. +//! `contractspecv0` is decoded as a bounded stream of official Stellar XDR +//! `ScSpecEntry` values. This crate never executes contract code. use std::io::Cursor; From a2794a3c271b3f737cb6d3a22f760d96e02a5a4d Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:44:42 +0100 Subject: [PATCH 49/57] docs: update appeal draft with completed integrations --- docs/DRIPS_APPLICATION_DRAFT.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/DRIPS_APPLICATION_DRAFT.md b/docs/DRIPS_APPLICATION_DRAFT.md index ed70cac..1c4d008 100644 --- a/docs/DRIPS_APPLICATION_DRAFT.md +++ b/docs/DRIPS_APPLICATION_DRAFT.md @@ -6,7 +6,7 @@ This is a factual draft for review before any application is submitted. It does StellarFoundry DevKit is a Rust developer toolkit for Stellar and Soroban. Its current implementation provides bounded decoding of `ScVal`, `TransactionEnvelope`, and `ContractEvent` XDR; Stellar strkey classification; RPC endpoint validation; a typed RPC protocol layer with deterministic mock transport; and a CLI named `stellar-foundry`. -The combined open implementation PR also adds bounded HTTP JSON-RPC transport, non-executing WASM structural inspection, deterministic structural findings, SARIF 2.1.0 serialization, strict TOML configuration APIs, and release/readiness workflows. These changes are still under review and should not be described as already merged or released. +The implementation branch also adds bounded HTTP JSON-RPC transport, non-executing WASM inspection, bounded decoding of contractspecv0 XDR entries, deterministic structural findings, SARIF 2.1.0 serialization and a manual code-scanning upload workflow, strict TOML configuration wired into `rpc-health`, and release/readiness workflows. These changes are still under review until PR #133 merges. ## Problem and ecosystem relevance @@ -25,10 +25,10 @@ The combined CI run passed formatting, Clippy, Linux, Windows, macOS, and Rust 1 ## Current limitations -- The `contractspecv0` custom section is detected and measured, but individual XDR entries are not yet decoded into typed function, struct, and enum metadata. +- `contractspecv0` entries are decoded with the official Stellar XDR types into XDR-JSON definitions, variant names, and best-effort names; a normalized ABI schema remains future work. - Security-analysis rules are structural heuristics, not a comprehensive vulnerability detector. -- SARIF serialization is implemented, but automatic GitHub code-scanning upload is not configured. -- Configuration parsing and environment overrides exist as a library API, but are not yet wired into every CLI command. +- SARIF can be uploaded using the manual workflow when a repository-relative WASM artifact exists and code-scanning permissions are available. +- RPC configuration is wired into `rpc-health`; other commands do not consume RPC settings. - The implementation PRs remain open. Do not state that all proposed features are already available on `main`. ## Contributor opportunities From f60442b15ecb1195dbc6eb8b3b5e3ee004057d20 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:45:38 +0100 Subject: [PATCH 50/57] style(cli): format config integration --- crates/devkit-cli/src/main.rs | 43 ++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index 1506d61..40994f5 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -233,9 +233,9 @@ fn run(cli: Cli) -> Result { } Command::RpcHealth { endpoint } => { let mut config = load_config(config_path.as_deref())?; - config.apply_environment().map_err(|message| { - CliError::new(EXIT_USAGE, "configuration", message) - })?; + config + .apply_environment() + .map_err(|message| CliError::new(EXIT_USAGE, "configuration", message))?; let endpoint = endpoint.or(config.rpc_url).ok_or_else(|| { CliError::new( EXIT_USAGE, @@ -245,11 +245,9 @@ fn run(cli: Cli) -> Result { })?; devkit_config::diagnostic(config.verbose, "rpc_health_start"); let endpoint = devkit_rpc::Endpoint::parse(&endpoint).map_err(runtime)?; - let transport = devkit_rpc::HttpTransport::new( - endpoint, - Duration::from_millis(config.timeout_ms), - ) - .map_err(runtime)?; + let transport = + devkit_rpc::HttpTransport::new(endpoint, Duration::from_millis(config.timeout_ms)) + .map_err(runtime)?; let health = devkit_rpc::Client::new(transport) .health() .map_err(runtime)?; @@ -300,7 +298,11 @@ fn load_config(path: Option<&str>) -> Result { const MAX_CONFIG_BYTES: u64 = 1024 * 1024; let mut config = if let Some(path) = path { let metadata = fs::metadata(path).map_err(|error| { - CliError::new(EXIT_ERROR, "io", format!("failed to inspect config file: {error}")) + CliError::new( + EXIT_ERROR, + "io", + format!("failed to inspect config file: {error}"), + ) })?; if metadata.len() > MAX_CONFIG_BYTES { return Err(CliError::new( @@ -310,7 +312,11 @@ fn load_config(path: Option<&str>) -> Result { )); } let content = fs::read_to_string(path).map_err(|error| { - CliError::new(EXIT_ERROR, "io", format!("failed to read config file: {error}")) + CliError::new( + EXIT_ERROR, + "io", + format!("failed to read config file: {error}"), + ) })?; if content.len() as u64 > MAX_CONFIG_BYTES { return Err(CliError::new( @@ -319,15 +325,14 @@ fn load_config(path: Option<&str>) -> Result { "config file exceeds the 1048576-byte limit", )); } - devkit_config::Config::parse_toml(&content).map_err(|message| { - CliError::new(EXIT_USAGE, "configuration", message) - })? + devkit_config::Config::parse_toml(&content) + .map_err(|message| CliError::new(EXIT_USAGE, "configuration", message))? } else { devkit_config::Config::default() }; - config.validate().map_err(|message| { - CliError::new(EXIT_USAGE, "configuration", message) - })?; + config + .validate() + .map_err(|message| CliError::new(EXIT_USAGE, "configuration", message))?; Ok(config) } @@ -427,7 +432,6 @@ fn runtime(error: devkit_core::DevkitError) -> CliError { CliError::new(EXIT_ERROR, category, error.to_string()) } - #[cfg(test)] mod tests { use super::*; @@ -445,7 +449,10 @@ mod tests { .expect("write temporary config"); let config = load_config(path.to_str()).expect("valid config"); let _ = fs::remove_file(&path); - assert_eq!(config.rpc_url.as_deref(), Some("https://soroban-testnet.stellar.org")); + assert_eq!( + config.rpc_url.as_deref(), + Some("https://soroban-testnet.stellar.org") + ); assert_eq!(config.timeout_ms, 2500); assert!(config.verbose); } From 5c270c8d45d0da3e33cd84ff931b47a3abc8497c Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:45:41 +0100 Subject: [PATCH 51/57] style(contract): format malformed spec test --- crates/devkit-contract/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs index 03c4ee7..24bfc30 100644 --- a/crates/devkit-contract/src/lib.rs +++ b/crates/devkit-contract/src/lib.rs @@ -204,5 +204,4 @@ mod tests { let error = inspect_wasm(&wasm).unwrap_err().to_string(); assert!(error.contains("contractspecv0 XDR entry"), "{error}"); } - } From 05afde967712a69a3a87bd321c755e2f6428bf51 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:45:45 +0100 Subject: [PATCH 52/57] build: sync lockfile with contract spec decoder --- Cargo.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 5a9858a..2733e8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,8 @@ version = "0.1.0" dependencies = [ "devkit-core", "serde", + "serde_json", + "stellar-xdr", "wasmparser", ] From a8b3a48fcdeb98b6f235cee371d7804b06a1275f Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:46:40 +0100 Subject: [PATCH 53/57] fix(contract): correct WASM test fixture bytes From e412df9372e191c60def697c542f734f055329b9 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:46:52 +0100 Subject: [PATCH 54/57] fix(contract): correct WASM test fixture bytes --- crates/devkit-contract/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/devkit-contract/src/lib.rs b/crates/devkit-contract/src/lib.rs index 24bfc30..ec93f4c 100644 --- a/crates/devkit-contract/src/lib.rs +++ b/crates/devkit-contract/src/lib.rs @@ -196,7 +196,7 @@ mod tests { fn rejects_malformed_contract_spec_xdr() { let name = b"contractspecv0"; let payload_len = 1 + name.len() + 1; - let mut wasm = b"\\0asm\\x01\\0\\0\\0".to_vec(); + let mut wasm = b"\0asm\x01\0\0\0".to_vec(); wasm.extend_from_slice(&[0, payload_len as u8, name.len() as u8]); wasm.extend_from_slice(name); wasm.push(0xff); From e10db5acbcec0bc0d4ff013ec30f88dfbf057f5d Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:47:26 +0100 Subject: [PATCH 55/57] docs: refresh changelog after remaining integrations --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9db0580..145e2cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 disabled, response-size limits, response validation, and structured errors. - CLI commands `inspect-wasm`, `analyze-wasm`, and `rpc-health`. - `rpc-health` now consumes bounded TOML configuration, timeout settings, and environment overrides; endpoint precedence is explicit in the CLI reference. -- Release-validation workflow, release checklist, and Drips readiness checklist. +- Release-validation workflow, release checklist, Drips readiness checklist, and manual SARIF upload workflow for GitHub code scanning. - RustSec dependency auditing and declared Rust 1.91 workspace MSRV in PR #127. - Malformed-XDR regression corpus and bounded-input hardening. - CI: formatting, Clippy, and Linux, Windows, and macOS tests. From a1ed7e545a541f22bd864d1663a4e7ad0878080f Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:47:36 +0100 Subject: [PATCH 56/57] docs: correct remaining capability notes --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 145e2cc..df82caa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,10 +35,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - These changes are on open pull-request branches and are not all merged to `main`. -- WASM contract specification entries are detected and sized, but individual - `contractspecv0` XDR entries are not yet decoded into typed metadata. +- `contractspecv0` XDR entries are decoded into official XDR-JSON definitions + with a variant kind and best-effort name; a normalized ABI schema remains future work. - Analysis findings are structural heuristics, not a comprehensive vulnerability detector. -- SARIF serialization is available; automated GitHub code-scanning upload is - not configured. +- SARIF upload requires manual workflow dispatch, a repository-relative WASM + artifact, and GitHub code-scanning permissions. - The configuration API is currently wired into `rpc-health`; other commands do not consume RPC configuration because they do not need it. From 2fff36ed9ade6c00a2daa413713e2b4b11ef77f1 Mon Sep 17 00:00:00 2001 From: Ayinkx <142127428+Ayinkx01@users.noreply.github.com> Date: Wed, 28 Oct 2026 12:48:47 +0100 Subject: [PATCH 57/57] fix(cli): remove unnecessary config mutability --- crates/devkit-cli/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index 40994f5..fd9e51f 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -296,7 +296,7 @@ fn doctor(format: OutputFormat) { /// independent of RPC-specific environment variables. fn load_config(path: Option<&str>) -> Result { const MAX_CONFIG_BYTES: u64 = 1024 * 1024; - let mut config = if let Some(path) = path { + let config = if let Some(path) = path { let metadata = fs::metadata(path).map_err(|error| { CliError::new( EXIT_ERROR,