Skip to content

Commit a740f39

Browse files
committed
canonicalize crate name before checking if it's reserved
Currently reserved crate names come from a text file in the source. There are only two entries which consider canonicalization (e.g. the list contains `compiler-rt` and `compiler_rt`). The fact that the name is not canonicalized allows crates like https://crates.io/crates/std to be uploaded. While this definitely isn't a vulnerability, since the registry does *not* canonicalize the name, we should still disallow crates to be uploaded which appear at the same path as a reserved name, and would prevent a crate with a reserved name from being uploaded later. I've moved the actual data out of the text file, and into the database, so we can have a constraint which verifies this at the closest possible location. (We can't use an actual check constraint, as that disallows subselects). The crate referenced above should be manually deleted.
1 parent 9e0aef3 commit a740f39

File tree

5 files changed

+59
-45
lines changed

5 files changed

+59
-45
lines changed

src/bin/delete-crate.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
#![deny(warnings)]
99

10-
#[macro_use]
1110
extern crate cargo_registry;
1211
extern crate postgres;
1312
extern crate time;

src/bin/migrate.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,43 @@ fn migrations() -> Vec<Migration> {
831831
tx.execute("DROP INDEX badges_crate_type", &[])?;
832832
Ok(())
833833
}),
834+
Migration::new(20170305095748, |tx| {
835+
tx.batch_execute("
836+
CREATE TABLE reserved_crate_names (
837+
name TEXT PRIMARY KEY
838+
);
839+
CREATE UNIQUE INDEX ON reserved_crate_names (canon_crate_name(name));
840+
INSERT INTO reserved_crate_names (name) VALUES
841+
('alloc'), ('arena'), ('ast'), ('builtins'), ('collections'),
842+
('compiler-builtins'), ('compiler-rt'), ('compiletest'), ('core'), ('coretest'),
843+
('debug'), ('driver'), ('flate'), ('fmt_macros'), ('grammar'), ('graphviz'),
844+
('macro'), ('macros'), ('proc_macro'), ('rbml'), ('rust-installer'), ('rustbook'),
845+
('rustc'), ('rustc_back'), ('rustc_borrowck'), ('rustc_driver'), ('rustc_llvm'),
846+
('rustc_resolve'), ('rustc_trans'), ('rustc_typeck'), ('rustdoc'), ('rustllvm'),
847+
('rustuv'), ('serialize'), ('std'), ('syntax'), ('test'), ('unicode');
848+
849+
CREATE FUNCTION ensure_crate_name_not_reserved() RETURNS trigger AS $$
850+
BEGIN
851+
IF canon_crate_name(NEW.name) IN (
852+
SELECT canon_crate_name(name) FROM reserved_crate_names
853+
) THEN
854+
RAISE EXCEPTION 'cannot upload crate with reserved name';
855+
END IF;
856+
RETURN NEW;
857+
END;
858+
$$ LANGUAGE plpgsql;
859+
860+
CREATE TRIGGER trigger_ensure_crate_name_not_reserved
861+
BEFORE INSERT OR UPDATE ON crates
862+
FOR EACH ROW EXECUTE PROCEDURE ensure_crate_name_not_reserved();
863+
")?;
864+
Ok(())
865+
}, |tx| {
866+
tx.execute("DROP TRIGGER trigger_ensure_crate_name_not_reserved ON crates", &[])?;
867+
tx.execute("DROP FUNCTION ensure_crate_name_not_reserved()", &[])?;
868+
tx.execute("DROP TABLE reserved_crate_names", &[])?;
869+
Ok(())
870+
}),
834871
];
835872
// NOTE: Generate a new id via `date +"%Y%m%d%H%M%S"`
836873

src/krate.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,11 @@ impl Crate {
152152
None => {}
153153
}
154154

155-
// Blacklist the current set of crates in the rust distribution
156-
const RESERVED: &'static str = include_str!("reserved_crates.txt");
157-
158-
if RESERVED.lines().any(|krate| name == krate) {
155+
let stmt = conn.prepare("SELECT 1 FROM reserved_crate_names
156+
WHERE canon_crate_name(name) =
157+
canon_crate_name($1)")?;
158+
let rows = stmt.query(&[&name])?;
159+
if !rows.is_empty() {
159160
return Err(human("cannot upload a crate with a reserved name"))
160161
}
161162

src/reserved_crates.txt

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/tests/krate.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,23 @@ fn new_krate() {
350350
assert_eq!(json.krate.max_version, "1.0.0");
351351
}
352352

353+
#[test]
354+
fn new_krate_with_reserved_name() {
355+
fn test_bad_name(name: &str) {
356+
let (_b, app, middle) = ::app();
357+
let mut req = ::new_req(app, name, "1.0.0");
358+
::mock_user(&mut req, ::user("foo"));
359+
let json = bad_resp!(middle.call(&mut req));
360+
assert!(json.errors[0].detail.contains("cannot upload a crate with a reserved name"));
361+
}
362+
363+
test_bad_name("std");
364+
test_bad_name("STD");
365+
test_bad_name("compiler-rt");
366+
test_bad_name("compiler_rt");
367+
test_bad_name("coMpiLer_Rt");
368+
}
369+
353370
#[test]
354371
fn new_krate_weird_version() {
355372
let (_b, app, middle) = ::app();

0 commit comments

Comments
 (0)