use std::error::Error as StdError;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use kcode_rust_source::Source;
const CONTAINERFILE: &str = include_str!("assets/Containerfile");
const CHROMIUM_OUTER_SANDBOX_CONFIG: &str = include_str!("assets/ChromiumOuterSandbox.conf");
static UNIQUE: AtomicU64 = AtomicU64::new(0);
pub struct Error(String);
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
fn new(category: &str, message: impl fmt::Display) -> Self {
Self(format!("{category}: {message}"))
}
fn io(operation: &str, path: impl AsRef<Path>, source: io::Error) -> Self {
Self::new(
"io",
format!("{operation} at {}: {source}", path.as_ref().display()),
)
}
fn redact(mut self, secret: &str) -> Self {
if !secret.is_empty() {
self.0 = self.0.replace(secret, "[REDACTED]");
}
self
}
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl fmt::Debug for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.debug_tuple("Error").field(&self.0).finish()
}
}
impl StdError for Error {}
pub fn check(source: &Source) -> Result<()> {
check_with(source, OsStr::new("podman"), &tool_image())
}
pub fn publish(source: &Source, crates_io_registry_token: &str) -> Result<()> {
publish_with(
source,
crates_io_registry_token,
OsStr::new("podman"),
&tool_image(),
)
}
fn check_with(source: &Source, podman: &OsStr, image: &str) -> Result<()> {
checked_run(source, podman, image).map(|_| ())
}
fn checked_run(source: &Source, podman: &OsStr, image: &str) -> Result<RunDirectory> {
ensure_image(podman, image)?;
let run = RunDirectory::new(source, "check")?;
for stage in stages() {
run_stage(podman, image, &run, stage)?;
}
Ok(run)
}
fn publish_with(source: &Source, token: &str, podman: &OsStr, image: &str) -> Result<()> {
let token = token.trim();
if token.is_empty() {
return Err(Error::new(
"invalid_token",
"the crates.io registry token is empty",
));
}
let run = checked_run(source, podman, image)?;
let mut command = podman_command(podman, &run, "/tmp", true);
command
.env("CARGO_REGISTRY_TOKEN", token)
.arg("--env=CARGO_REGISTRY_TOKEN")
.arg(image)
.arg("cargo")
.arg("--quiet")
.arg("--config")
.arg("registry.global-credential-providers=[\"cargo:token\"]")
.arg("publish")
.arg("--manifest-path=/workspace/Cargo.toml")
.arg("--registry=crates-io")
.arg("--no-verify")
.arg("--color=never");
run_checked(command, "publish", "cargo publish").map_err(|error| error.redact(token))
}
#[derive(Clone, Copy)]
struct Stage {
category: &'static str,
label: &'static str,
arguments: &'static [&'static str],
network: bool,
}
fn stages() -> [Stage; 6] {
[
Stage {
category: "check.fetch",
label: "cargo fetch",
arguments: &["fetch", "--color", "never"],
network: true,
},
Stage {
category: "check.format",
label: "cargo fmt",
arguments: &["fmt", "--all"],
network: false,
},
Stage {
category: "check.build",
label: "cargo build",
arguments: &[
"build",
"--workspace",
"--all-targets",
"--all-features",
"--locked",
"--offline",
"--color",
"never",
],
network: false,
},
Stage {
category: "check.clippy",
label: "cargo clippy",
arguments: &[
"clippy",
"--workspace",
"--all-targets",
"--all-features",
"--locked",
"--offline",
"--color",
"never",
"--",
"-D",
"warnings",
],
network: false,
},
Stage {
category: "check.test",
label: "cargo test",
arguments: &[
"test",
"--workspace",
"--all-targets",
"--all-features",
"--locked",
"--offline",
"--no-fail-fast",
"--color",
"never",
],
network: false,
},
Stage {
category: "check.doc_test",
label: "cargo doc tests",
arguments: &[
"test",
"--workspace",
"--all-features",
"--doc",
"--locked",
"--offline",
"--no-fail-fast",
"--color",
"never",
],
network: false,
},
]
}
fn run_stage(podman: &OsStr, image: &str, run: &RunDirectory, stage: Stage) -> Result<()> {
let mut command = podman_command(podman, run, "/workspace", stage.network);
command
.arg(image)
.arg("cargo")
.arg("--quiet")
.args(stage.arguments);
run_checked(command, stage.category, stage.label)
}
fn ensure_image(podman: &OsStr, image: &str) -> Result<()> {
let mut inspect = Command::new(podman);
inspect.arg("image").arg("exists").arg(image);
let inspected = run_capture(inspect, "inspect Podman image")?;
if inspected.status.success() {
return Ok(());
}
if inspected.status.code() != Some(1) {
return Err(command_error(
"sandbox.image",
"Podman image inspection",
inspected,
));
}
let build = TemporaryDirectory::new("image")?;
let containerfile = build.path().join("Containerfile");
fs::write(&containerfile, CONTAINERFILE)
.map_err(|error| Error::io("write embedded Containerfile", &containerfile, error))?;
let chromium_config = build.path().join("ChromiumOuterSandbox.conf");
fs::write(&chromium_config, CHROMIUM_OUTER_SANDBOX_CONFIG).map_err(|error| {
Error::io(
"write embedded Chromium outer-sandbox configuration",
&chromium_config,
error,
)
})?;
let mut command = Command::new(podman);
command
.arg("build")
.arg("--tag")
.arg(image)
.arg("--file")
.arg(&containerfile)
.arg(build.path());
run_checked(command, "sandbox.image", "Podman image build")
}
fn podman_command(podman: &OsStr, run: &RunDirectory, workdir: &str, network: bool) -> Command {
let mut command = Command::new(podman);
command
.arg("run")
.arg("--rm")
.arg("--read-only")
.arg("--cap-drop=all")
.arg("--security-opt=no-new-privileges")
.arg("--userns=keep-id")
.arg("--tmpfs=/tmp:rw,nosuid,nodev")
.arg("--workdir")
.arg(workdir)
.arg("--env=CARGO_HOME=/cargo-home")
.arg("--env=CARGO_TARGET_DIR=/target")
.arg("--env=CARGO_TERM_COLOR=never");
if !network {
command.arg("--network=none");
}
command
.arg("--pull=never")
.arg("--volume")
.arg(volume_spec(&run.workspace, "/workspace"))
.arg("--volume")
.arg(volume_spec(&run.cargo_home, "/cargo-home"))
.arg("--volume")
.arg(volume_spec(&run.target, "/target"));
command
}
fn volume_spec(source: &Path, destination: &str) -> OsString {
let mut specification = source.as_os_str().to_os_string();
specification.push(":");
specification.push(destination);
specification.push(":rw,Z");
specification
}
fn run_checked(command: Command, category: &'static str, label: &'static str) -> Result<()> {
let output = run_capture(command, label)?;
if output.status.success() {
Ok(())
} else {
Err(command_error(category, label, output))
}
}
fn run_capture(mut command: Command, label: &str) -> Result<CapturedOutput> {
let output = command
.output()
.map_err(|error| Error::new("sandbox.start", format!("could not run {label}: {error}")))?;
Ok(CapturedOutput {
status: output.status,
stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
})
}
fn command_error(category: &str, label: &str, output: CapturedOutput) -> Error {
let status = output
.status
.code()
.map_or_else(|| "signal".to_owned(), |code| code.to_string());
Error::new(
category,
format!(
"{label} exited with {status}\n--- stdout ---\n{}\n--- stderr ---\n{}",
output.stdout, output.stderr
),
)
}
struct CapturedOutput {
status: ExitStatus,
stdout: String,
stderr: String,
}
struct RunDirectory {
_root: TemporaryDirectory,
workspace: PathBuf,
cargo_home: PathBuf,
target: PathBuf,
}
impl RunDirectory {
fn new(source: &Source, label: &str) -> Result<Self> {
let root = TemporaryDirectory::new(label)?;
let workspace = root.path().join("workspace");
let cargo_home = root.path().join("cargo-home");
let target = root.path().join("target");
fs::create_dir(&workspace)
.map_err(|error| Error::io("create disposable workspace", &workspace, error))?;
materialize(&workspace, source)?;
fs::create_dir(&cargo_home)
.map_err(|error| Error::io("create disposable Cargo home", &cargo_home, error))?;
fs::create_dir(&target)
.map_err(|error| Error::io("create disposable target", &target, error))?;
Ok(Self {
_root: root,
workspace,
cargo_home,
target,
})
}
}
fn materialize(root: &Path, source: &Source) -> Result<()> {
for file in source.files() {
let destination = root.join(&file.path);
let parent = destination.parent().ok_or_else(|| {
Error::new(
"unsafe_path",
format!("source path has no parent: {:?}", file.path),
)
})?;
fs::create_dir_all(parent)
.map_err(|error| Error::io("create source parent", parent, error))?;
fs::write(&destination, file.contents.as_bytes())
.map_err(|error| Error::io("write source file", &destination, error))?;
}
Ok(())
}
struct TemporaryDirectory(PathBuf);
impl TemporaryDirectory {
fn new(label: &str) -> Result<Self> {
let parent = std::env::temp_dir().join("kcode-rust-library-toolchain");
fs::create_dir_all(&parent)
.map_err(|error| Error::io("create temporary work root", &parent, error))?;
loop {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
let counter = UNIQUE.fetch_add(1, Ordering::Relaxed);
let path = parent.join(format!(
"{label}-{}-{nanos:x}-{counter:x}",
std::process::id()
));
match fs::create_dir(&path) {
Ok(()) => return Ok(Self(path)),
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {}
Err(error) => {
return Err(Error::io("create temporary directory", path, error));
}
}
}
}
fn path(&self) -> &Path {
&self.0
}
}
impl Drop for TemporaryDirectory {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
fn tool_image() -> String {
let mut hash = 0xcbf29ce484222325_u64;
for byte in CONTAINERFILE
.bytes()
.chain(CHROMIUM_OUTER_SANDBOX_CONFIG.bytes())
{
hash ^= u64::from(byte);
hash = hash.wrapping_mul(0x100000001b3);
}
format!(
"localhost/kcode-rust-library-toolchain:{}-{hash:016x}",
env!("CARGO_PKG_VERSION")
)
}
#[cfg(all(test, unix))]
mod tests {
use std::ffi::OsStr;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use kcode_rust_source::{File, Source};
use super::{RunDirectory, check_with, podman_command, publish_with};
struct Fixture(PathBuf);
impl Fixture {
fn new(label: &str) -> Self {
let path = std::env::temp_dir().join(format!(
"kcode-rust-library-toolchain-test-{label}-{}-{}",
std::process::id(),
super::UNIQUE.fetch_add(1, super::Ordering::Relaxed)
));
fs::create_dir(&path).unwrap();
Self(path)
}
fn path(&self) -> &Path {
&self.0
}
fn script(&self, contents: &str) -> PathBuf {
let path = self.path().join("fake-podman");
fs::write(&path, contents).unwrap();
let mut permissions = fs::metadata(&path).unwrap().permissions();
permissions.set_mode(0o755);
fs::set_permissions(&path, permissions).unwrap();
path
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
fn source() -> Source {
Source::validate(
&[
File {
path: "Cargo.toml".to_owned(),
contents:
"[package]\nname = \"demo\"\nversion = \"0.1.0\"\nedition = \"2024\"\n"
.to_owned(),
},
File {
path: "Documentation.md".to_owned(),
contents: "docs\n".to_owned(),
},
File {
path: "src/lib.rs".to_owned(),
contents: String::new(),
},
],
"demo",
)
.unwrap()
}
#[test]
fn every_cargo_invocation_is_globally_quiet() {
let fixture = Fixture::new("quiet");
let log = fixture.path().join("arguments.log");
let podman = fixture.script(&format!(
"#!/bin/sh\nif [ \"$1\" = image ]; then exit 0; fi\nprintf '%s\\n' \"$*\" >> '{}'\nexit 0\n",
log.display()
));
check_with(&source(), podman.as_os_str(), "test-image").unwrap();
publish_with(&source(), "private-token", podman.as_os_str(), "test-image").unwrap();
let arguments = fs::read_to_string(log).unwrap();
let cargo_lines = arguments
.lines()
.filter(|line| line.contains(" cargo "))
.collect::<Vec<_>>();
assert_eq!(cargo_lines.len(), 13);
assert!(
cargo_lines
.iter()
.all(|line| line.contains("test-image cargo --quiet "))
);
assert!(
cargo_lines
.iter()
.any(|line| line.contains(" cargo --quiet fmt --all"))
);
assert!(!arguments.contains("private-token"));
assert!(!arguments.contains("fmt --all --check"));
}
#[test]
fn failed_commands_retain_large_complete_stdout_and_stderr() {
let fixture = Fixture::new("complete-output");
let podman = fixture.script(
"#!/bin/sh\nif [ \"$1\" = image ]; then exit 0; fi\n\
printf 'stdout-start\\n'\n\
i=0; while [ $i -lt 4000 ]; do printf '0123456789'; i=$((i+1)); done\n\
printf '\\nstdout-end\\n'\n\
printf 'stderr-start\\n' >&2\n\
i=0; while [ $i -lt 4000 ]; do printf 'abcdefghij' >&2; i=$((i+1)); done\n\
printf '\\nstderr-end\\n' >&2\n\
exit 1\n",
);
let rendered = check_with(&source(), podman.as_os_str(), "test-image")
.unwrap_err()
.to_string();
assert!(rendered.len() > 75 * 1024);
for marker in ["stdout-start", "stdout-end", "stderr-start", "stderr-end"] {
assert!(rendered.contains(marker));
}
assert!(!rendered.contains("truncated"));
}
#[test]
fn publication_redacts_the_token_across_complete_output() {
let fixture = Fixture::new("redaction");
let podman = fixture.script(
"#!/bin/sh\nif [ \"$1\" = image ]; then exit 0; fi\n\
case \"$*\" in\n\
*' publish '*)\n\
printf 'stdout-before %s stdout-after\\n' \"$CARGO_REGISTRY_TOKEN\"\n\
printf 'stderr-before %s stderr-after\\n' \"$CARGO_REGISTRY_TOKEN\" >&2\n\
exit 1\n\
;;\n\
esac\n\
exit 0\n",
);
let rendered = publish_with(&source(), "private-token", podman.as_os_str(), "test-image")
.unwrap_err()
.to_string();
assert!(!rendered.contains("private-token"));
assert_eq!(rendered.matches("[REDACTED]").count(), 2);
for marker in [
"stdout-before",
"stdout-after",
"stderr-before",
"stderr-after",
] {
assert!(rendered.contains(marker));
}
}
#[test]
fn image_and_outer_container_preserve_browser_hardening() {
assert!(super::CONTAINERFILE.contains("chromium"));
assert!(
super::CONTAINERFILE
.contains("COPY ChromiumOuterSandbox.conf /etc/chromium.d/kcode-outer-sandbox")
);
assert!(
super::CHROMIUM_OUTER_SANDBOX_CONFIG
.lines()
.any(|line| line == r#"CHROMIUM_FLAGS="$CHROMIUM_FLAGS --no-sandbox""#)
);
let run = RunDirectory::new(&source(), "outer-sandbox-test").unwrap();
let command = podman_command(OsStr::new("podman"), &run, "/workspace", false);
let arguments = command
.get_args()
.map(|argument| argument.to_string_lossy().into_owned())
.collect::<Vec<_>>();
for expected in [
"--read-only",
"--cap-drop=all",
"--security-opt=no-new-privileges",
"--userns=keep-id",
"--tmpfs=/tmp:rw,nosuid,nodev",
"--network=none",
] {
assert!(arguments.iter().any(|argument| argument == expected));
}
}
}