Repository: typst/typst
File: crates/typst-library/src/visualize/image/mod.rs (Rust)

Code before:
/// )
/// ```
#[elem(scope, Locatable, Tagged, Synthesize, LocalName, Figurable)]
pub struct ImageElem {
    /// A [path]($syntax/#paths) to an image file or raw bytes making up an
    /// image in one of the supported [formats]($image.format).
    ///
    /// Bytes can be used to specify raw pixel data in a row-major,
    /// left-to-right, top-to-bottom format.
    ///
    /// ```example
    /// #let original = read("diagram.svg")
    /// #let changed = original.replace(
    ///   "#2B80FF", // blue
    ///   green.to-hex(),
    /// )
    ///
    /// #image(bytes(original))
    /// #image(bytes(changed))
    /// ```
    #[required]
    #[parse(
        let source = args.expect::<Spanned<DataSource>>("source")?;
        if let DataSource::Path(path) = &source.v {
            if path.starts_with("http://") || path.starts_with("https://") {
                bail!(source.span, "network access is not supported"; hint: "download the image and place it as a file next to the document");
            }
        }
        let loaded = source.load(engine.world)?;
        Derived::new(source.v, loaded)
    )]
    pub source: Derived<DataSource, Loaded>,

    /// The image's format.
    ///
    /// By default, the format is detected automatically. Typically, you thus
    /// only need to specify this when providing raw bytes as the
    /// [`source`]($image.source) (even then, Typst will try to figure out the
    /// format automatically, but that's not always possible).
    ///
    /// Supported formats are `{"png"}`, `{"jpg"}`, `{"gif"}`, `{"svg"}`,
    /// `{"pdf"}`, `{"webp"}` as well as raw pixel data.
    ///
    /// Note that several restrictions apply when using PDF files as images:
    ///
    /// - When exporting to PDF, any PDF image file used must have a version
    ///   equal to or lower than the [export target PDF
    ///   version]($pdf/#pdf-versions).
    /// - PDF files as images are currently not supported when exporting with a
    ///   specific PDF standard, like PDF/A-3 or PDF/UA-1. In these cases, you
    ///   can instead use SVGs to embed vector images.

Review comment (security):
This currently uses `bail!` which would immediately return from the function and is a hard error.

I'm not certain this is desired as some targets (i.e. Linux) are fairly lax in what they allow within paths, we wouldn't want to impose restrictions beyond those necessitated by the security model. It seems currently Typst converts the `//` to `/`, which is sensible regarding correctness in POSIX / with multi-platform stuff. It also causes the tests added with this PR to result in valid reads however; placing a file at `http[s]:/example.com/image.png` lets them compile just fine. The patch is a regression in that sense as we now get an error.

We could just emit a `warning!` into `engine.sink.warn()` and let the fs error separately. I believe the hint could also be attached directly to the result of the read below, but I don't know how to do that on a `SourceResult`; it might have to be implemented within the cli's `World` itself.

Code after:
/// ```
#[elem(scope, Locatable, Tagged, Synthesize, LocalName, Figurable)]
pub struct ImageElem {
    /// A path to an image file or raw bytes making up an image in one of the
    /// supported [formats]($image.format).
    ///
    /// Bytes can be used to specify raw pixel data in a row-major,
    /// left-to-right, top-to-bottom format.
    ///
    /// ```example
    /// #let original = read("diagram.svg")
    /// #let changed = original.replace(
    ///   "#2B80FF", // blue
    ///   green.to-hex(),
    /// )
    ///
    /// #image(bytes(original))
    /// #image(bytes(changed))
    /// ```
    #[required]
    #[parse(
        let source = args.expect::<Spanned<DataSource>>("source")?;
        let loaded = source.load(engine.world)?;
        Derived::new(source.v, loaded)
    )]
    pub source: Derived<DataSource, Loaded>,

    /// The image's format.
    ///
    /// By default, the format is detected automatically. Typically, you thus
    /// only need to specify this when providing raw bytes as the
    /// [`source`]($image.source) (even then, Typst will try to figure out the
    /// format automatically, but that's not always possible).
    ///
    /// Supported formats are `{"png"}`, `{"jpg"}`, `{"gif"}`, `{"svg"}`,
    /// `{"pdf"}`, `{"webp"}` as well as raw pixel data.
    ///
    /// Note that several restrictions apply when using PDF files as images:
    ///
    /// - When exporting to PDF, any PDF image file used must have a version
    ///   equal to or lower than the [export target PDF
    ///   version]($pdf/#pdf-versions).
    /// - PDF files as images are currently not supported when exporting with a
    ///   specific PDF standard, like PDF/A-3 or PDF/UA-1. In these cases, you
    ///   can instead use SVGs to embed vector images.
    /// - The image file must not be password-protected.
    /// - Tags in your PDF image will not be preserved. Instead, you must
    ///   provide an [alternative description]($image.alt) to make the image
    ///   accessible.
    ///
    /// When providing raw pixel data as the `source`, you must specify a