commit 09c68259bab9a746c912f67b93724f139be800b5
parent 6a6f576e617e1fcca6c3fdbbd631cd627d4b621d
Author: triesap <tyson@radroots.org>
Date: Fri, 10 Jul 2026 03:50:33 +0000
cli: tighten SDK outcome label guard
- scope SDK outcome label source checks to the CLI helper spans
- require helper bodies to delegate through SDK enum as_str labels
- reject local match or wildcard outcome arms inside guarded helpers
- cover wildcard helper failures and unrelated unknown fallbacks
Diffstat:
| M | src/runtime/sdk.rs | | | 186 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
1 file changed, 175 insertions(+), 11 deletions(-)
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -812,6 +812,12 @@ mod tests {
required_tokens: &'static [&'static str],
}
+ struct SdkOutcomeLabelHelperGuard {
+ label: &'static str,
+ start: &'static str,
+ end: &'static str,
+ }
+
const DIRECT_RR_RS_DEPENDENCIES: &[DirectRrRsDependency] = &[
DirectRrRsDependency {
section: "dependencies",
@@ -1000,6 +1006,29 @@ mod tests {
),
];
+ const SDK_OUTCOME_LABEL_HELPER_GUARDS: &[SdkOutcomeLabelHelperGuard] = &[
+ SdkOutcomeLabelHelperGuard {
+ label: "push outbox transport outcome label helper",
+ start: "pub fn sdk_transport_outcome_kind_label(",
+ end: "pub fn sdk_target_outcome_kind_label(",
+ },
+ SdkOutcomeLabelHelperGuard {
+ label: "push outbox target outcome label helper",
+ start: "pub fn sdk_target_outcome_kind_label(",
+ end: "pub fn sdk_validation_transport_outcome_kind_label(",
+ },
+ SdkOutcomeLabelHelperGuard {
+ label: "validation transport outcome label helper",
+ start: "pub fn sdk_validation_transport_outcome_kind_label(",
+ end: "pub fn sdk_validation_relay_outcome_kind_label(",
+ },
+ SdkOutcomeLabelHelperGuard {
+ label: "validation relay outcome label helper",
+ start: "pub fn sdk_validation_relay_outcome_kind_label(",
+ end: "#[derive(Debug, Clone, PartialEq, Eq)]",
+ },
+ ];
+
const MIGRATED_CLI_PATH_GUARDS: &[MigratedCliPathGuard] = &[
MigratedCliPathGuard {
label: "listing publish",
@@ -1778,6 +1807,7 @@ mod tests {
match production_source_without_tests(&relative_path, &source) {
Ok(production_source) => sdk_outcome_label_contract_findings(
&relative_path,
+ source.as_str(),
production_source.as_str(),
),
Err(error) => vec![error],
@@ -1793,6 +1823,63 @@ mod tests {
}
#[test]
+ fn sdk_outcome_label_guard_rejects_wildcard_unknown_inside_helper_span() {
+ let source = sdk_outcome_label_helper_source(
+ concat!(
+ " match kind {\n",
+ " PushOutboxTransportOutcomeKind::Accepted => \"accepted\".to_owned(),\n",
+ " _ => \"unknown\".to_owned(),\n",
+ " }\n"
+ ),
+ "",
+ );
+ let production_source =
+ production_source_without_tests("fixture.rs", source.as_str()).expect("source");
+ let findings =
+ sdk_outcome_label_contract_findings("fixture.rs", source.as_str(), &production_source);
+
+ assert!(
+ findings
+ .iter()
+ .any(|finding| finding.contains("does not delegate to SDK enum as_str labels")),
+ "SDK outcome label guard must reject local helper rendering:\n{}",
+ findings.join("\n")
+ );
+ assert!(
+ findings
+ .iter()
+ .any(|finding| finding.contains("uses a wildcard outcome label arm")),
+ "SDK outcome label guard must reject wildcard unknown helper rendering:\n{}",
+ findings.join("\n")
+ );
+ }
+
+ #[test]
+ fn sdk_outcome_label_guard_allows_unrelated_unknown_fallbacks_outside_helper_spans() {
+ let source = sdk_outcome_label_helper_source(
+ " kind.as_str().to_owned()\n",
+ concat!(
+ "fn unrelated_status_label(value: Option<&str>) -> &str {\n",
+ " match value {\n",
+ " Some(value) => value,\n",
+ " _ => \"unknown\",\n",
+ " }\n",
+ "}\n"
+ ),
+ );
+ let production_source =
+ production_source_without_tests("fixture.rs", source.as_str()).expect("source");
+ let findings =
+ sdk_outcome_label_contract_findings("fixture.rs", source.as_str(), &production_source);
+
+ assert!(
+ findings.is_empty(),
+ "SDK outcome label guard must ignore unrelated unknown fallbacks:\n{}",
+ findings.join("\n")
+ );
+ }
+
+ #[test]
fn cli_account_create_sources_reject_implicit_identity_ingestion() {
let account_source = rust_code_without_non_code(
"src/runtime/account.rs",
@@ -2175,34 +2262,111 @@ mod tests {
.collect()
}
- fn sdk_outcome_label_contract_findings(label: &str, source: &str) -> Vec<String> {
+ fn sdk_outcome_label_contract_findings(
+ label: &str,
+ raw_source: &str,
+ production_source: &str,
+ ) -> Vec<String> {
let mut findings = SDK_OUTCOME_LABEL_SOURCE_DISALLOWED_TOKENS
.iter()
.flat_map(|(token, reason)| {
- source.match_indices(token).map(move |(index, _)| {
+ production_source.match_indices(token).map(move |(index, _)| {
format!(
"{label}:{} uses forbidden SDK outcome label token `{token}` for {reason}",
- line_number(source, index)
+ line_number(production_source, index)
)
})
})
.collect::<Vec<_>>();
- let wildcard_unknown = concat!("_ =>", " \"unknown\"");
- for enum_name in [
- "PushOutboxTargetOutcomeKind::",
- "TradeValidationReceiptNostrRelayOutcomeKind::",
- ] {
- if source.contains(enum_name) && source.contains(wildcard_unknown) {
+ findings.extend(sdk_outcome_label_helper_findings(label, raw_source));
+
+ findings
+ }
+
+ fn sdk_outcome_label_helper_findings(label: &str, source: &str) -> Vec<String> {
+ let mut findings = Vec::new();
+ for guard in SDK_OUTCOME_LABEL_HELPER_GUARDS {
+ let (start_index, end_index) =
+ match optional_source_segment_bounds(source, guard.start, guard.end) {
+ Ok(Some(bounds)) => bounds,
+ Ok(None) => continue,
+ Err(error) => {
+ findings.push(format!("{label}: {error}"));
+ continue;
+ }
+ };
+ let raw_segment = &source[start_index..end_index];
+ let code_segment =
+ rust_code_without_non_code(label, raw_segment).expect("SDK label helper source");
+ if !code_segment.contains("kind.as_str().to_owned()") {
+ findings.push(format!(
+ "{label}:{} {helper} does not delegate to SDK enum as_str labels",
+ line_number(source, start_index),
+ helper = guard.label
+ ));
+ }
+ if let Some(index) = code_segment.find("match kind") {
findings.push(format!(
- "{label} degrades known `{enum_name}` variants through wildcard unknown label rendering"
+ "{label}:{} {helper} matches SDK outcome variants locally",
+ line_number(source, start_index + index),
+ helper = guard.label
+ ));
+ }
+ if let Some(index) = code_segment.find("_ =>") {
+ findings.push(format!(
+ "{label}:{} {helper} uses a wildcard outcome label arm",
+ line_number(source, start_index + index),
+ helper = guard.label
));
}
}
-
findings
}
+ fn sdk_outcome_label_helper_source(transport_body: &str, extra_source: &str) -> String {
+ format!(
+ "pub fn sdk_transport_outcome_kind_label(kind: PushOutboxTransportOutcomeKind) -> String {{\n\
+{transport_body}\
+}}\n\
+pub fn sdk_target_outcome_kind_label(kind: PushOutboxTargetOutcomeKind) -> String {{\n\
+ kind.as_str().to_owned()\n\
+}}\n\
+pub fn sdk_validation_transport_outcome_kind_label(\n\
+ kind: TradeValidationReceiptNostrRelayTransportOutcomeKind,\n\
+) -> String {{\n\
+ kind.as_str().to_owned()\n\
+}}\n\
+pub fn sdk_validation_relay_outcome_kind_label(\n\
+ kind: TradeValidationReceiptNostrRelayOutcomeKind,\n\
+) -> String {{\n\
+ kind.as_str().to_owned()\n\
+}}\n\
+#[derive(Debug, Clone, PartialEq, Eq)]\n\
+struct FixtureConfig;\n\
+{extra_source}"
+ )
+ }
+
+ fn optional_source_segment_bounds(
+ source: &str,
+ start: &str,
+ end: &str,
+ ) -> Result<Option<(usize, usize)>, String> {
+ let Some(start_index) = source.find(start) else {
+ return Ok(None);
+ };
+ let Some(end_index) = source[start_index..]
+ .find(end)
+ .map(|index| start_index + index)
+ else {
+ return Err(format!(
+ "SDK outcome label helper source segment starting `{start}` is missing end marker `{end}`"
+ ));
+ };
+ Ok(Some((start_index, end_index)))
+ }
+
fn production_source_without_tests(path: &str, source: &str) -> Result<String, String> {
let code_source = rust_code_without_non_code(path, source)?;
let mut production_source = String::with_capacity(code_source.len());