Skip to content
This repository was archived by the owner on Aug 7, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
--ignore RUSTSEC-2025-0119
--ignore RUSTSEC-2025-0134
--ignore RUSTSEC-2026-0002
--ignore RUSTSEC-2026-0194
--ignore RUSTSEC-2026-0195
--ignore RUSTSEC-2026-0173

secrets:
name: Secrets Scan
Expand Down
17 changes: 16 additions & 1 deletion .github/workflows/stability.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ jobs:
--ignore RUSTSEC-2025-0119
--ignore RUSTSEC-2025-0134
--ignore RUSTSEC-2026-0002
--ignore RUSTSEC-2026-0194
--ignore RUSTSEC-2026-0195
--ignore RUSTSEC-2026-0173

deny:
name: Cargo Deny
Expand All @@ -108,4 +111,16 @@ jobs:
- uses: actions/checkout@v5
- name: Checkout Reflective sibling dependencies
run: bash scripts/ci/checkout-reflective-siblings.sh
- uses: EmbarkStudios/cargo-deny-action@v2
# Not EmbarkStudios/cargo-deny-action: its docker container can't
# install the repo's pinned musl toolchain and doesn't see the
# sibling checkouts, so `cargo metadata` fails on converge-core.
# Run cargo-deny in the runner env like the audit job above.
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-deny
- name: Run cargo-deny
run: cargo deny check
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ parking_lot = "0.12"
proptest = "1.5"
lancedb = { version = "0.26", default-features = false }
object_store = "0.13"
quick-xml = "0.39"
quick-xml = "0.41"
regex-lite = "0.1"
reqwest = { version = "0.12", features = ["blocking", "json", "rustls-tls", "stream"], default-features = false }
scraper = { version = "0.20", default-features = false }
Expand Down
5 changes: 4 additions & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ security-audit:
--ignore RUSTSEC-2025-0119 \
--ignore RUSTSEC-2024-0436 \
--ignore RUSTSEC-2025-0134 \
--ignore RUSTSEC-2026-0002
--ignore RUSTSEC-2026-0002 \
--ignore RUSTSEC-2026-0194 \
--ignore RUSTSEC-2026-0195 \
--ignore RUSTSEC-2026-0173
cargo deny check

# Session opener
Expand Down
15 changes: 8 additions & 7 deletions crates/manifold/src/feed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,13 +723,14 @@ fn read_text(
reader: &mut Reader<&[u8]>,
start: &BytesStart<'_>,
) -> Result<Option<String>, FeedError> {
reader
let text = reader
.read_text(start.name())
.map(|text| {
let trimmed = text.trim();
(!trimmed.is_empty()).then_some(trimmed.to_string())
})
.map_err(|error| FeedError::Parse(error.to_string()))
.map_err(|error| FeedError::Parse(error.to_string()))?;
let text = text
.decode()
.map_err(|error| FeedError::Parse(error.to_string()))?;
let trimmed = text.trim();
Ok((!trimmed.is_empty()).then_some(trimmed.to_string()))
}

fn local_name(name: &[u8]) -> String {
Expand All @@ -744,7 +745,7 @@ fn attr_value(reader: &Reader<&[u8]>, start: &BytesStart<'_>, key: &[u8]) -> Opt
.find(|attribute| local_name(attribute.key.as_ref()).as_bytes() == key)
.and_then(|attribute| {
attribute
.decode_and_unescape_value(reader.decoder())
.decoded_and_normalized_value(quick_xml::XmlVersion::Implicit1_0, reader.decoder())
.ok()
.map(std::borrow::Cow::into_owned)
})
Expand Down
6 changes: 5 additions & 1 deletion crates/manifold/src/model_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

use serde::{Deserialize, Serialize};

use crate::secret::{SecretProvider, default_secret_provider};
use crate::secret::default_secret_provider;
// has_secret is a SecretProvider trait method; its call sites are all behind
// provider feature gates, so the import is unused in a featureless build.
#[allow(unused_imports)]
use crate::secret::SecretProvider as _;
use converge_provider::LlmError;
use converge_provider::selection::{
AgentRequirements, ComplianceLevel, CostClass, DataSovereignty, ModelSelectorTrait,
Expand Down
2 changes: 1 addition & 1 deletion crates/manifold/src/secret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ pub fn default_secret_provider() -> &'static DefaultSecretProvider {
fn env_secrets_allowed() -> bool {
matches!(
std::env::var("MANIFOLD_ALLOW_ENV_SECRETS").ok().as_deref(),
Some("1") | Some("true") | Some("yes")
Some("1" | "true" | "yes")
)
}

Expand Down
6 changes: 6 additions & 0 deletions crates/manifold/src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ pub fn extract_first_text(xml: &str, local_name: &str) -> Result<Option<String>,
let text = reader
.read_text(name)
.map_err(|e| XmlExtractError::Parse(e.to_string()))?;
let text = text
.decode()
.map_err(|e| XmlExtractError::Parse(e.to_string()))?;
return Ok(Some(text.into_owned()));
}
}
Expand Down Expand Up @@ -83,6 +86,9 @@ pub fn extract_all_texts(xml: &str, local_name: &str) -> Result<Vec<String>, Xml
let text = reader
.read_text(name)
.map_err(|e| XmlExtractError::Parse(e.to_string()))?;
let text = text
.decode()
.map_err(|e| XmlExtractError::Parse(e.to_string()))?;
out.push(text.into_owned());
}
}
Expand Down
14 changes: 14 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ ignore = [
# lru 0.12.5 — IterMut unsoundness. Transitive via tantivy → lance-index;
# we do not call IterMut on lru caches, exposure path is upstream-internal.
"RUSTSEC-2026-0002",
# quick-xml 0.39.4 — duplicate-attribute quadratic run time (0194) and
# NsReader namespace-allocation DoS (0195), fixed in >=0.41. Our direct
# dep moved to 0.41 (2026-07-02); this pin is transitive via object_store
# 0.13.2 (latest available), which uses it to parse S3/Azure API list
# responses. Exposure is XML from the operator-configured storage
# provider's own API, not arbitrary attacker input, and both advisories
# are DoS-class (no memory unsafety). Drop both when object_store
# releases against quick-xml >=0.41.
"RUSTSEC-2026-0194",
"RUSTSEC-2026-0195",
# proc-macro-error2 2.0.1 unmaintained (advisory 2026-06-07). Transitive
# proc-macro, compile-time only, no runtime surface. No maintained
# upstream replacement path yet. Added 2026-07-02.
"RUSTSEC-2026-0173",
]

[licenses]
Expand Down
4 changes: 4 additions & 0 deletions kb/Planning/MILESTONES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
> **Archived 2026-07-02** — active milestone tracking moved to Linear (Reflective team).
> This file is kept for historical context only. Do not add new items here.

---
source: mixed
---
Expand Down Expand Up @@ -30,6 +33,7 @@ source: mixed
- [x] Tag v1.0.0.

## Open: pull-driven
**Epic:** E9

- [ ] Downstream proof that products register Manifold handles through
`converge_provider::ChatBackendRegistry`.
Expand Down