refactor(server): fold in PM review notes on classify_path
- classify_path now Rejects a collection slug containing '.' (mirrors OrgCollections::validate, plan L317, and item_path's documented contract, plan L990). Unreachable today since git normalizes './' away, but keeps the pre-receive hook self-defensive against path traversal. - Rename test item_write_nested_slug_takes_leading_segment_only -> item_write_nested_slug_is_rejected (it asserts Rejected; old name misled). - Add dotted_slug_is_rejected covering the new guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M5brcDrT35r5GaJySXD5ja
This commit is contained in:
@@ -32,6 +32,13 @@ pub fn classify_path(path: &str) -> PathClass {
|
||||
if slug.is_empty() {
|
||||
return PathClass::Rejected("empty collection slug in items path".to_string());
|
||||
}
|
||||
// Defense-in-depth: mirror `OrgCollections::validate` — a slug containing
|
||||
// '.' (e.g. a `..`/`.` path-traversal attempt) is structurally invalid.
|
||||
// git normalizes most `./` away before the hook sees the path, so this is
|
||||
// unreachable today; it keeps the hook self-defensive regardless.
|
||||
if slug.contains('.') {
|
||||
return PathClass::Rejected(format!("invalid collection slug: {:?}", slug));
|
||||
}
|
||||
return PathClass::Item { collection: slug.to_string() };
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ fn item_write_yields_collection_slug() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_write_nested_slug_takes_leading_segment_only() {
|
||||
// Slugs cannot contain '/', so a 4-segment path is malformed → Rejected.
|
||||
fn item_write_nested_slug_is_rejected() {
|
||||
// Slugs cannot contain '/', so a path with extra segments is malformed → Rejected.
|
||||
assert_eq!(
|
||||
classify_path("items/prod/sub/x.enc"),
|
||||
PathClass::Rejected("items path must be items/<slug>/<id>.enc".to_string())
|
||||
@@ -51,6 +51,16 @@ fn empty_slug_segment_is_rejected() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dotted_slug_is_rejected() {
|
||||
// Defense-in-depth (mirrors OrgCollections::validate): a slug containing '.'
|
||||
// — e.g. a ".."/"." path-traversal attempt — is rejected.
|
||||
assert_eq!(
|
||||
classify_path("items/../x.enc"),
|
||||
PathClass::Rejected("invalid collection slug: \"..\"".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
use relicario_server::extract_schema_version;
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user