Skip to content

[security] maybe compile fail or running unsafe/untrust code due to Rust comments are executable in doctests #1060

Description

@loynoir

bug

[security] maybe compile fail or running unsafe/untrust code due to Rust comments are executable in doctests

reproduce

use std::collections::HashMap;

pub mod generated {
    typify::import_types!(schema = "./src/schema.json");
}

pub fn minimal_reproduce_library() -> String {
    format!(
        "{:?} {:?}",
        generated::Tmpfs(HashMap::default()),
        generated::PoC(HashMap::default())
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = minimal_reproduce_library();
        assert_eq!(result, "Tmpfs({}) PoC({})");
    }
}
`./src/schema.yaml`

https://github.com/moby/moby/blob/582d1cf3c43b892181ed3b8af3d353565f3f2f63/api/docs/v1.55.yaml#L1242-L1252

$defs:
  Tmpfs:
    type: "object"
    description: |
      A map of container directories which should be replaced by tmpfs
      mounts, and their corresponding mount options. For example:

      ```
      { "/run": "rw,noexec,nosuid,size=65536k" }
      ```
    additionalProperties:
      type: "string"
  PoC:
    type: "object"
    description: |
      ```
      panic!(">>>>>>>>>> Here's a PoC showing you can be pwned this way.")
      ```
    additionalProperties:
      type: "string"
`./src/schema.json`
{
  "$defs": {
    "Tmpfs": {
      "type": "object",
      "description": "A map of container directories which should be replaced by tmpfs\nmounts, and their corresponding mount options. For example:\n\n```\n{ \"/run\": \"rw,noexec,nosuid,size=65536k\" }\n```\n",
      "additionalProperties": {
        "type": "string"
      }
    },
    "PoC": {
      "type": "object",
      "description": "```\npanic!(\">>>>>>>>>> Here's a PoC showing you can be pwned this way.\")\n```\n",
      "additionalProperties": {
        "type": "string"
      }
    }
  }
}
expanded
// ...

#[doc = "```\npanic!(\">>>>>>>>>> Here's a PoC showing you can be pwned this way.\")\n```\n"]
#[doc = r""]
#[doc = r" <details><summary>JSON schema</summary>"]
#[doc = r""]
#[doc = r" ```json"]
#[doc = "{"]
#[doc = "  \"description\": \"```\\npanic!(\\\">>>>>>>>>> Here's a PoC showing you can be pwned this way.\\\")\\n```\\n\","]
#[doc = "  \"type\": \"object\","]
#[doc = "  \"additionalProperties\": {"]
#[doc = "    \"type\": \"string\""]
#[doc = "  }"]
#[doc = "}"]
#[doc = r" ```"]
#[doc = r" </details>"]
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct PoC(pub ::std::collections::HashMap<::std::string::String, ::std::string::String>);

/// ...

#[doc = "A map of container directories which should be replaced by tmpfs\nmounts, and their corresponding mount options. For example:\n\n```\n{ \"/run\": \"rw,noexec,nosuid,size=65536k\" }\n```\n"]
#[doc = r""]
#[doc = r" <details><summary>JSON schema</summary>"]
#[doc = r""]
#[doc = r" ```json"]
#[doc = "{"]
#[doc = "  \"description\": \"A map of container directories which should be replaced by tmpfs\\nmounts, and their corresponding mount options. For example:\\n\\n```\\n{ \\\"/run\\\": \\\"rw,noexec,nosuid,size=65536k\\\" }\\n```\\n\","]
#[doc = "  \"type\": \"object\","]
#[doc = "  \"additionalProperties\": {"]
#[doc = "    \"type\": \"string\""]
#[doc = "  }"]
#[doc = "}"]
#[doc = r" ```"]
#[doc = r" </details>"]
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Tmpfs(pub ::std::collections::HashMap<::std::string::String, ::std::string::String>);

// ...

actual

$ cargo test
...

   Doc-tests reproduce

running 2 tests
test src/lib.rs - generated::Tmpfs (line 7) ... FAILED
test src/lib.rs - generated::PoC (line 4) ... FAILED

failures:

---- src/lib.rs - generated::Tmpfs (line 7) stdout ----
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `:`
 --> src/lib.rs:8:9
  |
8 | { "/run": "rw,noexec,nosuid,size=65536k" }
  |         ^ expected one of `.`, `;`, `?`, `}`, or an operator

error: aborting due to 1 previous error

Couldn't compile the test.
---- src/lib.rs - generated::PoC (line 4) stdout ----
Test executable failed (exit status: 101).

stderr:

thread 'main' (xxx) panicked at src/lib.rs:3:1:
>>>>>>>>>> Here's a PoC showing you can be pwned this way.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace



failures:
    src/lib.rs - generated::PoC (line 4)
    src/lib.rs - generated::Tmpfs (line 7)

test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.15s

all doctests ran in 0.16s; merged doctests compilation took 0.02s
error: doctest failed, to rerun pass `--doc`

expected

Should derive ZERO doctest by default.

Like --lib output.

$ cargo test --lib
running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

And opt-in to allow doctest

pub mod generated {
    unsafe {
      typify::import_types!(
        schema = "./src/schema.json",
        unsafe_allow_doctest = true,
      );
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions