Reproduction steps:
- Fire any module command whose output includes a JSON-encoded string field longer than 300 characters (e.g. a command returning a list of several items serialized as a JSON string in the output).
- Inspect the persisted, sealed receipt's
outputfield for that command. - Try to
json.loads()the affected field from the receipt.
Expected: The receipt's output should either preserve the full field, or truncate in a way that leaves valid JSON (or clearly mark it as non-JSON truncated text) — not silently produce a field that looks like JSON but isn't.
Actual: approval_airlock.py's redact() cuts any output string over 300 characters to value[:300] + "…" uniformly, with no awareness of whether the string is JSON. A real, correct command output (e.g. a JSON array of channel objects) gets cut mid-string, so the persisted receipt's copy of that field fails json.loads(), even though the command itself returned complete, correct data. The cryptographic binding (post_execution_result_hash) is computed over the real untruncated data and is unaffected — only the human-readable/consumable copy in the receipt breaks. This will affect any module whose command output includes a JSON-string field over 300 characters, not just ours.
Root cause: the 300-character truncation in redact() is applied as a blanket string operation with no check for whether the value is JSON before cutting it.
Suggested fix: either raise the truncation limit for structured/JSON-shaped output fields, or truncate at a JSON-aware boundary (e.g. truncate the underlying data structure before serializing, not the serialized string itself), or explicitly mark truncated JSON fields as such rather than leaving them looking well-formed.
Station version (railcall version): station-v1.5.8
Module slug + version: muhammad-akif-janjua/slack-guard v0.4.1