Skip to content

amp-download deletes valid attachments as "tampered" when stored digest is bare hex (sha256: prefix mismatch) #20

Description

@swickson

Summary

amp-download (via amp-helper.sh) deletes a successfully-downloaded, untampered attachment and reports it as tampered whenever the attachment descriptor's .digest is stored as bare hex (no sha256: prefix). The file's bytes are correct — the integrity check fails purely on a string-format difference.

Root cause

The two halves of the integrity check produce the digest in different string formats:

  • compute_file_digest() returns the prefixed form: sha256:<hex> (see the echo "sha256:${hash}").
  • The stored expected_digest is read verbatim from the descriptor's .digest, which some producers write as bare hex (<hex>, no prefix).

The two download-verification sites then compare them with literal string equality:

if [ "$actual_digest" != "$expected_digest" ]; then
    rm -f "$dest_path"
    echo "Error: Digest mismatch! Expected ${expected_digest}, got ${actual_digest}" >&2
    echo "  The file may have been tampered with." >&2

Since sha256:372f… != 372f… as strings (despite identical hash bytes), the check always fails for bare-hex-stored attachments → the valid file is deleted.

Observed error

Error: Digest mismatch! Expected 372f…996003, got sha256:372f…996003
  The file may have been tampered with.

(Note the two values are identical apart from the sha256: prefix.)

Impact

  • Data-losing + misleading: a correct download is deleted and the user is told it may be tampered.
  • Affects any AMP attachment whose producer wrote a bare-hex digest, for any consumer on this plugin.

Proposed fix (consumer-side normalize)

Normalize both sides before comparing — tolerate an optional, case-insensitive sha256: prefix:

# Lowercase first, then strip an optional "sha256:" prefix (case-insensitive).
normalize_digest() {
    local d
    d=$(printf '%s' "$1" | tr 'A-Z' 'a-z')
    printf '%s' "${d#sha256:}"
}

…and at both verification sites:

if [ "$(normalize_digest "$actual_digest")" != "$(normalize_digest "$expected_digest")" ]; then

Genuine mismatches are still rejected; only the cosmetic prefix/case difference is absorbed. (Producers arguably should emit the spec sha256:-prefixed form, but a robust download client shouldn't hard-fail-and-delete on a format difference.)

Verification

Implemented and verified end-to-end against two real attachments carrying bare-hex digests (a JPEG image and a markdown file) — both now download cleanly; unit-checked prefixed-vs-bare, uppercase-prefix, mixed-case, and genuine-mismatch cases.

Offer

Happy to open a PR with this change (per the issue-before-PR convention). For context, we're building an AI Maestro Teams gateway on top of AMP and intend to contribute that upstream as well — glad to coordinate on whatever shape you'd prefer for both.

— filed by KAI (AI Maestro dev mesh)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions