feat(api): cancel authorized exports via loopback CLI - #446
Conversation
Publish tepp-export-cancel cancel so operators mint naruon POST
/v1/exports/{export_id}/cancel onto spawned tepp-loopback TCP. Receipts stay
metric-free cancelled=true. LineageWeave is refused. NaruonLiveService stays
POST-only. ADR 0078.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| let parsed = ExportCancelled::from_json(&response.body)?; | ||
| if !parsed.cancelled { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| parsed.to_json() |
There was a problem hiding this comment.
🟡 Wrong-export cancellation reports success
When a valid receipt names another export, render_export_cancel_cli_stdout prints it as success. The requested export can remain active while operators see cancellation.
| let parsed = ExportCancelled::from_json(&response.body)?; | |
| if !parsed.cancelled { | |
| return Err(ApiError::InvalidWirePayload); | |
| } | |
| parsed.to_json() | |
| let parsed = ExportCancelled::from_json(&response.body)?; | |
| if !parsed.cancelled || parsed.export_id != invocation.export_id { | |
| return Err(ApiError::InvalidWirePayload); | |
| } | |
| parsed.to_json() |
Was this helpful? React with 👍 or 👎 to provide feedback.
| let mut bytes = Vec::new(); | ||
| stream | ||
| .read_to_end(&mut bytes) | ||
| .map_err(|error| map_io_error(&error))?; |
There was a problem hiding this comment.
🔴 Unbounded CLI input exhausts memory
A continuously writing peer makes read_to_end grow memory without limit. Piped stdin has the same flaw, so either stream can terminate the CLI.
Prompt for agents
Bound both untrusted input paths in crates/tepp_api/src/export_cancel_cli.rs. execute_export_cancel_cli currently reads the entire TCP response with read_to_end, and read_export_cancel_cli_stdin reads all piped stdin with read_to_string. Introduce a bounded reader that consumes at most the applicable wire limit plus one byte, returns ApiError::LimitExceeded on overflow, and preserves UTF-8 validation for stdin. Bound the HTTP response by the maximum allowed response header plus ExportCancelled/error-envelope body size before parsing. Add tests with over-limit streams and a reader that keeps producing data.
Was this helpful? React with 👍 or 👎 to provide feedback.
| fn parse_http_response(bytes: &[u8]) -> Result<NaruonLiveResponse, ApiError> { | ||
| let text = std::str::from_utf8(bytes).map_err(|_| ApiError::InvalidWirePayload)?; | ||
| let (header_block, body) = text | ||
| .split_once("\r\n\r\n") | ||
| .ok_or(ApiError::InvalidWirePayload)?; | ||
| let mut lines = header_block.split("\r\n"); | ||
| let status_line = lines.next().ok_or(ApiError::InvalidWirePayload)?; | ||
| let mut parts = status_line.split(' '); | ||
| if parts.next() != Some("HTTP/1.1") { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| let code = parts | ||
| .next() | ||
| .ok_or(ApiError::InvalidWirePayload)? | ||
| .parse::<u16>() | ||
| .map_err(|_| ApiError::InvalidWirePayload)?; | ||
| let reason_phrase = match code { | ||
| 200 => "OK", | ||
| 202 => "Accepted", | ||
| 400 => "Bad Request", | ||
| 403 => "Forbidden", | ||
| 413 => "Payload Too Large", | ||
| 422 => "Unprocessable Entity", | ||
| _ => return Err(ApiError::InvalidWirePayload), | ||
| }; | ||
| let mut content_length = None; | ||
| for line in lines { | ||
| let (name, value) = line.split_once(':').ok_or(ApiError::InvalidWirePayload)?; | ||
| if name.eq_ignore_ascii_case("content-length") { | ||
| if content_length.is_some() { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| content_length = Some( | ||
| value | ||
| .trim() | ||
| .parse::<usize>() | ||
| .map_err(|_| ApiError::InvalidWirePayload)?, | ||
| ); | ||
| } | ||
| } | ||
| let declared = content_length.ok_or(ApiError::InvalidWirePayload)?; | ||
| if declared != body.len() { | ||
| return Err(ApiError::InvalidWirePayload); | ||
| } | ||
| Ok(NaruonLiveResponse { | ||
| status_code: code, | ||
| reason_phrase, | ||
| body: body.to_owned(), | ||
| }) | ||
| } |
|
Closing with replacement mapping to #174. This CLI depends on #445's unauthenticated export mutation and cannot repair that trust boundary. Preserve its operator parsing/framing/stdout/refusal tests for the future authenticated export-operations CLI after principal/resource/purpose authorization exists. |
Summary
GAP-003A unique operator-visible slice: published
tepp-export-cancel cancelmints typednaruon_export_cancel_exchangeonto spawnedtepp-loopbackTCP.from_args+ typed exchange +loopback_http1_from_export_cancel_exchange+dispatch/execute/render+ published[[bin]].localhosthost /httporigin / unpublished consumer / credential flags fail closed.cancelled=true. Tenant, principal, source text, RMSE, bias, coverage, SE-gate, andtepp.scientific_acceptance.v1never appear.tepp-export-list(feat(api): consolidate export collection GET and CLI #444) ortepp-export-get(feat(api): consolidate export retrieval GET and CLI #417).feat/export-cancel-http-gap-003a). Does not unstack onto main.Explicitly not
Another export cancel HTTP (#445), export collection CLI (#444), export collection GET (#443), interpretation-run cancel CLI (#442), interpretation-run cancel HTTP (#440), interpretation-run collection CLI (#436), export-retrieval CLI (#417), export retrieval GET (#411), export-authorize CLI (#410), analysis-run cancel (#361), Leiden, Driver p.16, GAP-010 Figma/export, persistence/Compose (#287).
Does not add GET to
NaruonLiveService. Does not open LineageWeave on this naruon-owned adapter. Does not weaken fail-closed. Does not infer causality. HTTP 200 is not an ADR 0014 claim.Test plan
cargo test -p tepp_api --lib export_cancel_clicargo test -p tepp_api --test export_cancel_cli_contractcargo clippy -p tepp_api --all-targets -- -D warningscargo doc -p tepp_api --no-depspython3 scripts/validate_documentation.pypython3 scripts/check_docstrings.py