tests/release_server_test.rs
Ref: Size: 33.2 KiB History
mod common;
use std::process::Output;
use common::ServerHarness;
#[test]
fn ssh_client_interop_rejects_unknown_command() {
let harness = ServerHarness::new("release-canary");
harness.push_head();
let output = harness.ssh_exec("frobnicate");
assert!(
!output.status.success(),
"unknown exec command must fail, got: {}",
String::from_utf8_lossy(&output.stdout)
);
// A bogus exec command failing isn't proof the interop worked - ssh itself
// failing to connect/authenticate would also produce a non-zero exit. Rule
// that out explicitly so this canary actually proves client<->server auth
// and exec dispatch succeeded.
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stderr.contains("Permission denied"),
"ssh authentication failed, interop is broken: {}",
stderr
);
assert!(
!stderr.contains("Connection refused"),
"ssh failed to connect, interop is broken: {}",
stderr
);
}
fn stdout(output: &Output) -> String {
String::from_utf8_lossy(&output.stdout).to_string()
}
/// Pull a single header value out of a raw response head block, e.g.
/// `extract_header(&head, "etag")`. Case-insensitive on the header name.
fn extract_header(head: &str, name: &str) -> String {
let prefix = format!("{}:", name.to_lowercase());
head.lines()
.find(|l| l.to_lowercase().starts_with(&prefix))
.unwrap_or_else(|| panic!("missing header {} in:\n{}", name, head))
.split_once(':')
.unwrap()
.1
.trim()
.to_string()
}
/// Every header line except `Date` (which legitimately varies request to
/// request), for comparing that two responses are otherwise byte-identical.
fn strip_date_header(head: &str) -> String {
head.lines()
.filter(|l| !l.to_lowercase().starts_with("date:"))
.collect::<Vec<_>>()
.join("\n")
}
/// The exact HTTP status line (e.g. `"HTTP/1.1 200 OK"`), so a caller can't
/// be fooled by another header happening to contain the same digits as the
/// status code it's checking for.
fn status_line(head: &str) -> &str {
head.lines().next().unwrap_or("")
}
fn assert_ssh_error(output: &Output, needle: &str) {
assert!(!output.status.success(), "expected failure, got success");
let all = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
all.contains(needle),
"expected '{}' in output: {}",
needle,
all
);
}
#[test]
fn upload_stores_file_with_checksum() {
let harness = ServerHarness::new("release-upload");
harness.push_head();
let content = b"fake tarball bytes";
let output = harness.ssh_exec_with_stdin(
"collab-release upload 'release-upload.git' 'v1.0.0' 'app.tar.gz'",
content,
);
assert!(output.status.success(), "upload failed: {:?}", output);
// reply is "ok <sha256>"
let reply = stdout(&output);
let sha = reply
.trim()
.strip_prefix("ok ")
.expect("reply not 'ok <sha>'");
use sha2::Digest;
let expected: String = sha2::Sha256::digest(content)
.iter()
.map(|b| format!("{:02x}", b))
.collect();
assert_eq!(sha, expected);
let stored = harness
.repos_dir()
.join("release-upload.git/collab/releases/v1.0.0/app.tar.gz");
assert_eq!(std::fs::read(&stored).unwrap(), content);
assert!(stored.with_file_name("app.tar.gz.sha256").exists());
}
#[test]
fn duplicate_upload_needs_force() {
let harness = ServerHarness::new("release-dup");
harness.push_head();
let cmd = "collab-release upload 'release-dup.git' 'v1' 'a.tar.gz'";
assert!(harness.ssh_exec_with_stdin(cmd, b"one").status.success());
let dup = harness.ssh_exec_with_stdin(cmd, b"two");
assert_ssh_error(&dup, "already exists");
let forced = harness.ssh_exec_with_stdin(
"collab-release upload 'release-dup.git' 'v1' 'a.tar.gz' --force",
b"two",
);
assert!(
forced.status.success(),
"forced upload failed: {:?}",
forced
);
let stored = harness
.repos_dir()
.join("release-dup.git/collab/releases/v1/a.tar.gz");
assert_eq!(std::fs::read(&stored).unwrap(), b"two");
}
#[test]
fn list_returns_json_index() {
let harness = ServerHarness::new("release-list");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-list.git' 'v1.0.0' 'a.tar.gz'",
b"aaa",
);
let output = harness.ssh_exec("collab-release list 'release-list.git'");
assert!(output.status.success());
let index: serde_json::Value = serde_json::from_str(&stdout(&output)).unwrap();
let versions = index["versions"].as_array().unwrap();
assert_eq!(versions.len(), 1);
assert_eq!(versions[0]["version"], "v1.0.0");
assert_eq!(versions[0]["files"][0]["name"], "a.tar.gz");
assert_eq!(versions[0]["files"][0]["size"], 3);
assert_eq!(
versions[0]["files"][0]["sha256"].as_str().unwrap().len(),
64
);
}
#[test]
fn delete_removes_file_then_version() {
let harness = ServerHarness::new("release-del");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-del.git' 'v1' 'a.tar.gz'",
b"a",
);
harness.ssh_exec_with_stdin(
"collab-release upload 'release-del.git' 'v1' 'b.tar.gz'",
b"b",
);
let releases = harness.repos_dir().join("release-del.git/collab/releases");
let del_file = harness.ssh_exec("collab-release delete 'release-del.git' 'v1' 'a.tar.gz'");
assert!(del_file.status.success());
assert!(!releases.join("v1/a.tar.gz").exists());
assert!(releases.join("v1/b.tar.gz").exists());
let del_version = harness.ssh_exec("collab-release delete 'release-del.git' 'v1'");
assert!(del_version.status.success());
assert!(!releases.join("v1").exists());
let missing = harness.ssh_exec("collab-release delete 'release-del.git' 'v1'");
assert_ssh_error(&missing, "not found");
}
#[test]
fn write_policy_gates_upload_and_delete_but_not_list() {
let harness = ServerHarness::new("release-policy");
harness.push_head();
// Seed a release while the policy is still permissive, so the read-only
// assertions below have something real to observe. Without this, "list
// succeeds" and "delete was denied" would both be vacuously true against
// an empty releases dir.
let seeded = harness.ssh_exec_with_stdin(
"collab-release upload 'release-policy.git' 'v1' 'a.tar.gz'",
b"seed",
);
assert!(seeded.status.success(), "seed upload failed: {:?}", seeded);
harness.write_repo_server_policy(
"visibility = \"public\"\n[access]\nread = [\"*\"]\nwrite = []\n",
);
// Denied writes report "repository not found" - unknown and unauthorized
// deliberately share a reply so the error can't be used to probe which
// repos exist.
let upload = harness.ssh_exec_with_stdin(
"collab-release upload 'release-policy.git' 'v2' 'b.tar.gz'",
b"x",
);
assert_ssh_error(&upload, "repository not found");
let delete = harness.ssh_exec("collab-release delete 'release-policy.git' 'v1'");
assert_ssh_error(&delete, "repository not found");
let list = harness.ssh_exec("collab-release list 'release-policy.git'");
assert!(list.status.success(), "list must be allowed for readers");
// The seeded release is still listed: proof the denied delete didn't run.
let index: serde_json::Value = serde_json::from_str(&stdout(&list)).unwrap();
let versions = index["versions"].as_array().unwrap();
assert_eq!(versions.len(), 1, "seeded release must survive: {}", index);
assert_eq!(versions[0]["version"], "v1");
// And on disk: the seed is intact, the denied upload left nothing behind.
let releases = harness
.repos_dir()
.join("release-policy.git/collab/releases");
assert_eq!(
std::fs::read(releases.join("v1/a.tar.gz")).unwrap(),
b"seed"
);
assert!(!releases.join("v2/b.tar.gz").exists());
assert!(!releases.join("v2").exists());
}
#[test]
fn git_push_and_clone_over_ssh() {
// Exercises exec_request's git branch, stdin forwarding through data(),
// and channel_eof against a real git client - the path the release
// dispatch had to be threaded through without regressing.
let harness = ServerHarness::new("release-git");
let ssh_command = harness.ssh_command_string();
harness.work_repo().git(&[
"-c",
&format!("core.sshCommand={}", ssh_command),
"push",
&harness.repo_ssh_url(),
"main",
]);
let expected = harness.work_repo().git(&["rev-parse", "HEAD"]);
let expected = expected.trim();
let clone_dir = tempfile::TempDir::new().unwrap();
let dest = clone_dir.path().join("clone");
// Reuse the work repo's isolated HOME so the developer's real git config
// can't influence the clone.
let mut clone_cmd = std::process::Command::new("git");
harness.work_repo().apply_env(&mut clone_cmd);
let output = clone_cmd
.args([
"-c",
&format!("core.sshCommand={}", ssh_command),
"clone",
&harness.repo_ssh_url(),
dest.to_str().unwrap(),
])
.output()
.expect("failed to run git clone");
assert!(
output.status.success(),
"clone over ssh failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let mut head_cmd = std::process::Command::new("git");
harness.work_repo().apply_env(&mut head_cmd);
let cloned_head = head_cmd
.args(["rev-parse", "HEAD"])
.current_dir(&dest)
.output()
.expect("failed to run git rev-parse in clone");
assert_eq!(
String::from_utf8_lossy(&cloned_head.stdout).trim(),
expected,
"cloned repo is not at the pushed commit"
);
}
#[test]
fn oversize_upload_rejected_without_partial_file() {
let harness = ServerHarness::new_with_extra_config("release-size", "max_release_size = 16\n");
harness.push_head();
let output = harness.ssh_exec_with_stdin(
"collab-release upload 'release-size.git' 'v1' 'big.tar.gz'",
&[0u8; 64],
);
assert_ssh_error(&output, "maximum release size");
let version_dir = harness
.repos_dir()
.join("release-size.git/collab/releases/v1");
assert!(!version_dir.join("big.tar.gz").exists());
if version_dir.exists() {
assert_eq!(std::fs::read_dir(&version_dir).unwrap().count(), 0);
}
}
#[test]
fn invalid_names_and_unknown_repo_rejected() {
let harness = ServerHarness::new("release-invalid");
harness.push_head();
let traversal = harness.ssh_exec_with_stdin(
"collab-release upload 'release-invalid.git' '../evil' 'a.tar.gz'",
b"x",
);
assert!(!traversal.status.success());
let unknown =
harness.ssh_exec_with_stdin("collab-release upload 'nope.git' 'v1' 'a.tar.gz'", b"x");
assert!(!unknown.status.success());
}
#[test]
fn http_releases_page_and_download() {
let harness = ServerHarness::new("release-http");
harness.push_head();
let content: Vec<u8> = (0u32..600).flat_map(|i| i.to_le_bytes()).collect(); // binary body
harness.ssh_exec_with_stdin(
"collab-release upload 'release-http.git' 'v2.0.0' 'app.tar.gz'",
&content,
);
let page = harness.get_ok("/release-http/releases");
assert!(page.body.contains("v2.0.0"));
assert!(page.body.contains("app.tar.gz"));
assert!(page
.body
.contains("/release-http/releases/v2.0.0/app.tar.gz"));
let (head, body) = harness.get_bytes("/release-http/releases/v2.0.0/app.tar.gz");
assert!(head.contains("200"), "download failed: {}", head);
assert!(head.to_lowercase().contains("application/octet-stream"));
assert!(head
.to_lowercase()
.contains(&format!("content-length: {}", content.len())));
assert_eq!(body, content);
// checksum companion is downloadable as text
let (sha_head, sha_body) = harness.get_bytes("/release-http/releases/v2.0.0/app.tar.gz.sha256");
assert!(sha_head.contains("200"));
assert!(String::from_utf8(sha_body).unwrap().contains("app.tar.gz"));
}
#[test]
fn http_release_download_404s() {
let harness = ServerHarness::new("release-http-404");
harness.push_head();
let missing = harness.get("/release-http-404/releases/v9/none.tar.gz");
assert!(missing.status_line.contains("404"));
let traversal = harness.get("/release-http-404/releases/v9/..%2f..%2fconfig");
assert!(!traversal.status_line.contains("200"));
let page = harness.get_ok("/release-http-404/releases");
assert!(page.body.contains("No releases"));
}
#[test]
fn http_releases_respect_repo_policy() {
let harness = ServerHarness::new("release-http-private");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-http-private.git' 'v1' 'a.tar.gz'",
b"secret",
);
harness.write_repo_server_policy(
"visibility = \"private\"\n[ui]\nanonymous = false\n[http]\nanonymous_clone = false\n[access]\nread = [\"*\"]\nwrite = [\"*\"]\n",
);
let page = harness.get("/release-http-private/releases");
assert!(page.status_line.contains("404"));
let (head, _) = harness.get_bytes("/release-http-private/releases/v1/a.tar.gz");
assert!(
head.contains("404"),
"private artifact must not be served: {}",
head
);
}
/// The releases page is gated by allows_anonymous_ui; artifact downloads are
/// gated by allows_anonymous_http. These are separate gates: a policy that
/// allows the UI page but disables anonymous clone/download must still let
/// the page render (listing versions) while 404ing the actual download. This
/// pins the distinction so `release_download` can't accidentally be changed
/// to use `allows_anonymous_ui` instead of `allows_anonymous_http`.
#[test]
fn http_release_page_and_download_gates_are_independent() {
let harness = ServerHarness::new("release-http-gates");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-http-gates.git' 'v1' 'a.tar.gz'",
b"gated",
);
harness.write_repo_server_policy(
"visibility = \"public\"\n[ui]\nanonymous = true\n[http]\nanonymous_clone = false\n[access]\nread = [\"*\"]\nwrite = [\"*\"]\n",
);
let page = harness.get_ok("/release-http-gates/releases");
assert!(page.body.contains("v1"));
assert!(page.body.contains("a.tar.gz"));
let (head, _) = harness.get_bytes("/release-http-gates/releases/v1/a.tar.gz");
assert!(
head.contains("404"),
"download must stay gated by allows_anonymous_http: {}",
head
);
}
#[test]
fn http_release_range_request_returns_partial_content() {
let harness = ServerHarness::new("release-range");
harness.push_head();
let content: Vec<u8> = (0u16..=255).map(|b| b as u8).collect(); // 256 deterministic bytes
harness.ssh_exec_with_stdin(
"collab-release upload 'release-range.git' 'v1' 'app.bin'",
&content,
);
let (head, body) = harness.get_with_headers(
"/release-range/releases/v1/app.bin",
&[("Range", "bytes=10-19")],
);
assert!(head.contains("206"), "expected 206, got: {}", head);
assert!(
head.to_lowercase()
.contains("content-range: bytes 10-19/256"),
"missing/wrong content-range: {}",
head
);
assert!(
head.to_lowercase().contains("content-length: 10"),
"wrong content-length: {}",
head
);
assert_eq!(body, content[10..20]);
}
#[test]
fn http_release_range_suffix_request() {
let harness = ServerHarness::new("release-range-suffix");
harness.push_head();
let content: Vec<u8> = (0u16..=255).map(|b| b as u8).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-range-suffix.git' 'v1' 'app.bin'",
&content,
);
let (head, body) = harness.get_with_headers(
"/release-range-suffix/releases/v1/app.bin",
&[("Range", "bytes=-10")],
);
assert!(head.contains("206"), "expected 206, got: {}", head);
assert!(
head.to_lowercase()
.contains("content-range: bytes 246-255/256"),
"missing/wrong content-range: {}",
head
);
assert_eq!(body, content[246..256]);
}
#[test]
fn http_release_range_open_ended_request() {
let harness = ServerHarness::new("release-range-open");
harness.push_head();
let content: Vec<u8> = (0u16..=255).map(|b| b as u8).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-range-open.git' 'v1' 'app.bin'",
&content,
);
let (head, body) = harness.get_with_headers(
"/release-range-open/releases/v1/app.bin",
&[("Range", "bytes=250-")],
);
assert!(head.contains("206"), "expected 206, got: {}", head);
assert!(
head.to_lowercase()
.contains("content-range: bytes 250-255/256"),
"missing/wrong content-range: {}",
head
);
assert_eq!(body, content[250..256]);
}
#[test]
fn http_release_range_out_of_range_returns_416() {
let harness = ServerHarness::new("release-range-416");
harness.push_head();
let content: Vec<u8> = (0u16..=255).map(|b| b as u8).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-range-416.git' 'v1' 'app.bin'",
&content,
);
let (head, _) = harness.get_with_headers(
"/release-range-416/releases/v1/app.bin",
&[("Range", "bytes=1000-2000")],
);
assert!(head.contains("416"), "expected 416, got: {}", head);
assert!(
head.to_lowercase().contains("content-range: bytes */256"),
"missing/wrong content-range: {}",
head
);
}
#[test]
fn http_release_range_malformed_or_multi_falls_back_to_full_response() {
let harness = ServerHarness::new("release-range-bad");
harness.push_head();
let content: Vec<u8> = (0u16..=255).map(|b| b as u8).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-range-bad.git' 'v1' 'app.bin'",
&content,
);
// Multi-range: ignore the header, serve the full 200 (always spec-legal).
let (head, body) = harness.get_with_headers(
"/release-range-bad/releases/v1/app.bin",
&[("Range", "bytes=0-9,20-29")],
);
assert!(
head.contains("200"),
"expected full 200 for multi-range: {}",
head
);
assert_eq!(body, content);
// Malformed: same fallback.
let (head2, body2) = harness.get_with_headers(
"/release-range-bad/releases/v1/app.bin",
&[("Range", "not-a-range")],
);
assert!(
head2.contains("200"),
"expected full 200 for malformed range: {}",
head2
);
assert_eq!(body2, content);
}
#[test]
fn http_release_download_advertises_accept_ranges() {
let harness = ServerHarness::new("release-accept-ranges");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-accept-ranges.git' 'v1' 'a.tar.gz'",
b"hello world",
);
let (head, _) = harness.get_bytes("/release-accept-ranges/releases/v1/a.tar.gz");
assert!(head.contains("200"));
assert!(
head.to_lowercase().contains("accept-ranges: bytes"),
"missing accept-ranges: {}",
head
);
}
#[test]
fn http_release_conditional_get_returns_304() {
let harness = ServerHarness::new("release-etag");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-etag.git' 'v1' 'a.tar.gz'",
b"hello world",
);
let (head, _) = harness.get_bytes("/release-etag/releases/v1/a.tar.gz");
assert!(head.contains("200"));
assert!(
head.to_lowercase().contains("etag:"),
"missing etag: {}",
head
);
assert!(
head.to_lowercase().contains("last-modified:"),
"missing last-modified: {}",
head
);
assert!(
head.to_lowercase().contains("cache-control:"),
"missing cache-control: {}",
head
);
let etag = extract_header(&head, "etag");
let (head2, body2) = harness.get_with_headers(
"/release-etag/releases/v1/a.tar.gz",
&[("If-None-Match", &etag)],
);
assert!(head2.contains("304"), "expected 304, got: {}", head2);
assert!(body2.is_empty(), "304 must not have a body: {:?}", body2);
}
#[test]
fn http_release_if_none_match_weak_comparison_matches() {
// RFC 7232 §2.3.2: If-None-Match uses weak comparison, so a client that
// stored our strong ETag but replays it with a "W/" prefix (or a client
// that legitimately received a weak tag from an intermediary) must still
// get a 304, not a spurious full re-download.
let harness = ServerHarness::new("release-weak-inm");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-weak-inm.git' 'v1' 'a.tar.gz'",
b"hello world",
);
let (head, _) = harness.get_bytes("/release-weak-inm/releases/v1/a.tar.gz");
let etag = extract_header(&head, "etag");
let weak_etag = format!("W/{}", etag);
let (head2, body2) = harness.get_with_headers(
"/release-weak-inm/releases/v1/a.tar.gz",
&[("If-None-Match", &weak_etag)],
);
assert!(
head2.contains("304"),
"expected 304 for weak-compared matching etag, got: {}",
head2
);
assert!(body2.is_empty());
}
#[test]
fn http_release_if_modified_since_round_trip() {
let harness = ServerHarness::new("release-ims");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-ims.git' 'v1' 'a.tar.gz'",
b"hello world",
);
let (head, _) = harness.get_bytes("/release-ims/releases/v1/a.tar.gz");
assert!(head.contains("200"));
let last_modified = extract_header(&head, "last-modified");
let (head2, body2) = harness.get_with_headers(
"/release-ims/releases/v1/a.tar.gz",
&[("If-Modified-Since", &last_modified)],
);
assert!(head2.contains("304"), "expected 304, got: {}", head2);
assert!(body2.is_empty(), "304 must not have a body: {:?}", body2);
}
#[test]
fn http_release_range_single_byte_at_start() {
let harness = ServerHarness::new("release-range-single");
harness.push_head();
let content: Vec<u8> = (0u16..=255).map(|b| b as u8).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-range-single.git' 'v1' 'app.bin'",
&content,
);
let (head, body) = harness.get_with_headers(
"/release-range-single/releases/v1/app.bin",
&[("Range", "bytes=0-0")],
);
assert!(head.contains("206"), "expected 206, got: {}", head);
assert!(
head.to_lowercase().contains("content-range: bytes 0-0/256"),
"missing/wrong content-range: {}",
head
);
assert_eq!(body, vec![content[0]]);
}
#[test]
fn http_release_zero_length_artifact() {
let harness = ServerHarness::new("release-zero-len");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-zero-len.git' 'v1' 'empty.bin'",
b"",
);
// A normal GET on a zero-length file is a 200 with an empty body.
let (head, body) = harness.get_bytes("/release-zero-len/releases/v1/empty.bin");
assert!(head.contains("200"), "expected 200, got: {}", head);
assert!(body.is_empty());
// Any byte range on a zero-length file is unsatisfiable.
let (range_head, _) = harness.get_with_headers(
"/release-zero-len/releases/v1/empty.bin",
&[("Range", "bytes=0-0")],
);
assert!(
range_head.contains("416"),
"expected 416, got: {}",
range_head
);
assert!(
range_head
.to_lowercase()
.contains("content-range: bytes */0"),
"missing/wrong content-range: {}",
range_head
);
}
/// Reproduces the review finding directly: a client fetches the first half
/// of an artifact, the artifact is `--force`-replaced with different bytes
/// of the same length, and the client resumes presenting the validator it
/// still holds via `If-Range`. The server must detect the mismatch and
/// serve the full, current artifact rather than splicing old and new bytes
/// into a single response.
#[test]
fn http_release_if_range_etag_falls_back_to_full_after_force_replace() {
let harness = ServerHarness::new("release-if-range-stale");
harness.push_head();
let original: Vec<u8> = (0u8..20).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-if-range-stale.git' 'v1' 'app.bin'",
&original,
);
let (head1, body1) = harness.get_with_headers(
"/release-if-range-stale/releases/v1/app.bin",
&[("Range", "bytes=0-9")],
);
assert!(head1.contains("206"), "expected 206, got: {}", head1);
assert_eq!(body1, original[0..10]);
let stale_etag = extract_header(&head1, "etag");
let replaced: Vec<u8> = (100u8..120).collect();
let force = harness.ssh_exec_with_stdin(
"collab-release upload 'release-if-range-stale.git' 'v1' 'app.bin' --force",
&replaced,
);
assert!(force.status.success(), "force upload failed: {:?}", force);
let (head2, body2) = harness.get_with_headers(
"/release-if-range-stale/releases/v1/app.bin",
&[("Range", "bytes=10-19"), ("If-Range", &stale_etag)],
);
assert_eq!(
status_line(&head2),
"HTTP/1.1 200 OK",
"expected a full 200 when If-Range is stale (never splice), got: {}",
head2
);
assert_eq!(
body2, replaced,
"must serve the full current artifact, not a splice of old+new bytes"
);
// Sanity: the "assembled from a stale resume" bytes really would not
// have matched anything real, proving this isn't a vacuous check.
let mut spliced = original[0..10].to_vec();
spliced.extend_from_slice(&replaced[10..20]);
assert_ne!(body2, spliced);
}
#[test]
fn http_release_if_range_etag_matching_honors_range() {
let harness = ServerHarness::new("release-if-range-match");
harness.push_head();
let content: Vec<u8> = (0u8..20).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-if-range-match.git' 'v1' 'app.bin'",
&content,
);
let (head1, _) = harness.get_bytes("/release-if-range-match/releases/v1/app.bin");
let etag = extract_header(&head1, "etag");
let (head2, body2) = harness.get_with_headers(
"/release-if-range-match/releases/v1/app.bin",
&[("Range", "bytes=10-19"), ("If-Range", &etag)],
);
assert!(
head2.contains("206"),
"expected 206 when If-Range matches, got: {}",
head2
);
assert_eq!(body2, content[10..20]);
}
#[test]
fn http_release_if_range_date_form_match_honors_range() {
let harness = ServerHarness::new("release-if-range-date-match");
harness.push_head();
let content: Vec<u8> = (0u8..20).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-if-range-date-match.git' 'v1' 'app.bin'",
&content,
);
let (head1, _) = harness.get_bytes("/release-if-range-date-match/releases/v1/app.bin");
let last_modified = extract_header(&head1, "last-modified");
let (head2, body2) = harness.get_with_headers(
"/release-if-range-date-match/releases/v1/app.bin",
&[("Range", "bytes=10-19"), ("If-Range", &last_modified)],
);
assert!(
head2.contains("206"),
"expected 206 when If-Range date matches, got: {}",
head2
);
assert_eq!(body2, content[10..20]);
}
#[test]
fn http_release_if_range_date_form_mismatch_falls_back_to_full() {
let harness = ServerHarness::new("release-if-range-date-bad");
harness.push_head();
let content: Vec<u8> = (0u8..20).collect();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-if-range-date-bad.git' 'v1' 'app.bin'",
&content,
);
let (head, body) = harness.get_with_headers(
"/release-if-range-date-bad/releases/v1/app.bin",
&[
("Range", "bytes=10-19"),
("If-Range", "Sun, 06 Nov 1994 08:49:37 GMT"),
],
);
assert_eq!(
status_line(&head),
"HTTP/1.1 200 OK",
"expected full 200 for a non-matching If-Range date, got: {}",
head
);
assert_eq!(body, content);
}
/// Byte-identical 404s for policy-denied repos is a deliberate security
/// property (private repos must not be distinguishable from missing ones).
/// This pins that the property survives regardless of which conditional or
/// range headers a probing client sends — currently guarded only by the
/// early-return ordering in `release_download`, which a refactor could move.
///
/// The comparison baseline is a repo that was never created at all (`None`
/// out of `crate::repos::resolve`), not another response from the denied
/// repo itself — the property under test is "denied is indistinguishable
/// from absent," and only an independently-absent repo proves that.
///
/// The conditional-header variants are chosen to include the two that can
/// actually produce a *different* status code (304) if the policy gate ever
/// moved below the conditional-GET check: `If-None-Match: *` unconditionally
/// matches any existing resource, and a future `If-Modified-Since` is always
/// satisfied. `"whatever"`/a fixed past date can never yield anything but
/// a plain miss, so on their own they can't detect that particular
/// regression — both kinds are included so this test still means something
/// if either check is later changed.
#[test]
fn http_release_download_404_is_identical_regardless_of_conditional_headers() {
let harness = ServerHarness::new("release-404-headers");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-404-headers.git' 'v1' 'a.tar.gz'",
b"secret",
);
harness.write_repo_server_policy(
"visibility = \"private\"\n[ui]\nanonymous = false\n[http]\nanonymous_clone = false\n[access]\nread = [\"*\"]\nwrite = [\"*\"]\n",
);
let denied_path = "/release-404-headers/releases/v1/a.tar.gz";
// This repo was never created; resolve() returns None for it regardless
// of any conditional header, which is exactly the ground truth "absent"
// response the denied repo must stay indistinguishable from.
let nonexistent_path = "/release-404-headers-does-not-exist/releases/v1/a.tar.gz";
let (absent_head, absent_body) = harness.get_bytes(nonexistent_path);
assert_eq!(status_line(&absent_head), "HTTP/1.1 404 Not Found");
let variants: &[&[(&str, &str)]] = &[
&[("Range", "bytes=0-9")],
&[("If-None-Match", "\"whatever\"")],
&[("If-None-Match", "*")],
&[("If-Modified-Since", "Sun, 06 Nov 1994 08:49:37 GMT")],
// A real Monday: chrono's `%a` validates the weekday name, so a
// wrong day would silently fail to parse and make this variant
// vacuous (indistinguishable from the past-date case above).
&[("If-Modified-Since", "Mon, 01 Jan 2035 00:00:00 GMT")],
&[("If-Range", "\"whatever\"")],
];
for headers in variants {
let (head, body) = harness.get_with_headers(denied_path, headers);
assert_eq!(
status_line(&head),
"HTTP/1.1 404 Not Found",
"denied repo status differs from absent for {:?}: {}",
headers,
head
);
assert_eq!(
strip_date_header(&head),
strip_date_header(&absent_head),
"denied-repo response headers differ from a nonexistent repo's for {:?}",
headers
);
assert_eq!(
body, absent_body,
"denied-repo body differs from a nonexistent repo's for {:?}",
headers
);
}
}
#[test]
fn http_release_download_sets_nosniff() {
let harness = ServerHarness::new("release-nosniff");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-nosniff.git' 'v1' 'a.tar.gz'",
b"hello",
);
let (head, _) = harness.get_bytes("/release-nosniff/releases/v1/a.tar.gz");
assert!(
head.to_lowercase()
.contains("x-content-type-options: nosniff"),
"missing nosniff header: {}",
head
);
}
#[test]
fn http_releases_page_hides_download_links_when_downloads_disabled() {
let harness = ServerHarness::new("release-deadlink");
harness.push_head();
harness.ssh_exec_with_stdin(
"collab-release upload 'release-deadlink.git' 'v1' 'a.tar.gz'",
b"content",
);
harness.write_repo_server_policy(
"visibility = \"public\"\n[ui]\nanonymous = true\n[http]\nanonymous_clone = false\n[access]\nread = [\"*\"]\nwrite = [\"*\"]\n",
);
let page = harness.get_ok("/release-deadlink/releases");
assert!(page.body.contains("v1"));
assert!(page.body.contains("a.tar.gz"));
assert!(
!page
.body
.contains("href=\"/release-deadlink/releases/v1/a.tar.gz\""),
"download link must not be rendered when downloads are disabled:\n{}",
page.body
);
let lower = page.body.to_lowercase();
assert!(
lower.contains("not") && lower.contains("available"),
"expected a note that downloads are unavailable:\n{}",
page.body
);
}