Skip to content

fix(toolchain)!: restrict named toolchain characters - #4932

Open
cachebag wants to merge 5 commits into
rust-lang:mainfrom
cachebag:main
Open

fix(toolchain)!: restrict named toolchain characters#4932
cachebag wants to merge 5 commits into
rust-lang:mainfrom
cachebag:main

Conversation

@cachebag

@cachebag cachebag commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Partially addresses #4059 by restricting what characters a named toolchain may contain.

Named toolchains are now validated against the UTS #39 general security profile.

In essence; unicode_security::GeneralSecurityProfile::identifier_allowed() vs this hand-rolled pattern we had before. Letters and digits in any script stay legal, so 合法的 still works, as do ., _ and -. Whitespace, most punctuation, emoji, and invisible or direction-altering characters are rejected. I also exclude : (NTFS alternate data streams) and ' (shell quoting), plus . and .., which the profile allows.

This is breaking for unusual names like foo#bar or names with spaces. However it doesn't touch the rust-toolchain.toml diagnostics. Confusables are also still unresolved (i.e.μ vs µ, precomposed vs decomposed é).

@cachebag

Copy link
Copy Markdown
Contributor Author

Note to myself that this will probably need some corrections once #4930 is merged

@cachebag
cachebag force-pushed the main branch 4 times, most recently from 1318f92 to 9118fcc Compare June 30, 2026 14:11
Comment thread src/toolchain/names.rs
Comment thread src/toolchain/names.rs Outdated
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@rami3l

rami3l commented Jun 30, 2026

Copy link
Copy Markdown
Member

@cachebag Thanks for this PR!

I think given your comment in #4932 (comment) I'll probably review #4930 first and come back to this one later, many thanks for your understanding 🙏

Comment thread src/toolchain/names.rs Outdated
Comment thread src/toolchain/names.rs Outdated
Comment thread src/toolchain/names.rs
@cachebag
cachebag force-pushed the main branch 7 times, most recently from 86f5e64 to b8c8bb9 Compare June 30, 2026 21:07
@cachebag
cachebag requested a review from djc June 30, 2026 21:09
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@cachebag
cachebag force-pushed the main branch 4 times, most recently from 913f977 to a8ed506 Compare June 30, 2026 23:07
Comment thread src/toolchain/names.rs Outdated
Comment thread src/toolchain/names.rs Outdated
Comment thread src/toolchain/names.rs
Comment thread src/toolchain/names.rs Outdated
Comment thread src/toolchain/names.rs Outdated
}
}

fn is_legal_named_toolchain(candidate: &str) -> bool {

@rami3l rami3l Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: If we decide to keep it, it looks like it should be called is_legal_toolchain_name().

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above responses.

I do like is_legal_toolchain_name() more than is_valid_...()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? We have a bunch of validate_* functions to I like that is_valid_ references that more clearly than introducing a seemingly separate concept of "legality".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"legal" seemed to reflect the new allowlist/policy. i'm not hard pressed on the naming convention, is_valid_ is better for your reasons, that makes sense to me from a code standpoint

Comment thread src/toolchain/names.rs Outdated
Comment thread src/toolchain/names.rs Outdated
@cachebag
cachebag requested a review from rami3l July 1, 2026 13:10
@rami3l rami3l changed the title fix(toolchain): restrict named toolchain characters fix(toolchain)!: restrict named toolchain characters Jul 1, 2026
@cachebag
cachebag force-pushed the main branch 2 times, most recently from b1e3812 to 39fbbcc Compare July 1, 2026 23:59
@rami3l rami3l self-assigned this Jul 3, 2026
@rami3l

rami3l commented Jul 3, 2026

Copy link
Copy Markdown
Member

@cachebag Sorry for the delay in reviewing! I have to see if the current code can be further streamlined and what we should do about #4059 (comment). Please stay tuned...

@cachebag
cachebag marked this pull request as draft July 6, 2026 01:26
@cachebag
cachebag force-pushed the main branch 6 times, most recently from 8e4516c to 39f608c Compare July 13, 2026 13:01
@cachebag
cachebag force-pushed the main branch 3 times, most recently from d14a77d to 4259f6e Compare July 20, 2026 00:46
@rustbot

This comment has been minimized.

@cachebag
cachebag force-pushed the main branch 2 times, most recently from 0bc07d2 to 2eecf4a Compare August 3, 2026 13:57
Validate named toolchains with the UTS rust-lang#39 general security profile via
`unicode_security::GeneralSecurityProfile::identifier_allowed()` instead
of an ASCII allowlist, following the approach sketched in rust-lang#4059.

Letters and digits in any script are now legal, so `合法的` works as a
custom toolchain name. ASCII letters, digits, `.`, `_` and `-` remain
allowed, so every official toolchain name still parses. Whitespace, most
punctuation, emoji, and invisible or direction-altering characters such
as U+202E RIGHT-TO-LEFT OVERRIDE are still rejected.

Two characters the profile permits are excluded anyway: `:`, because a
named toolchain becomes a directory under `.rustup/toolchains` and
`name:stream` denotes an NTFS alternate data stream on Windows, and `'`,
which needs quoting in too many shells to be worth allowing. `.` and
`..` are rejected explicitly, since the profile permits both.

Confusables remain unresolved: `μ` is accepted while `µ` is not, and
precomposed and decomposed `é` are distinct names. That matches the
existing status quo.
`try_from_str!()` fed `TryFrom<String>`, `TryFrom<&str>` and `FromStr` off a
single inherent `validate()`, which is why each name type had one. With the
macro gone every such method had exactly one caller, its own `from_str()`, so
move the bodies there and drop the methods.

Rename the free `validate()` to `normalize_name()`, which is precisely what it does:
strips a `+` prefix, trims trailing slashes, and rejects empty names.
@cachebag
cachebag marked this pull request as ready for review August 3, 2026 14:40

@rami3l rami3l left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM modulo some tiny nits, nice work! 🙏

View changes since this review

Comment thread src/toolchain/names.rs
/// Strips the trailing slashes a shell may have completed onto a toolchain
/// directory, and rejects the `+toolchain` argument form along with names that
/// are empty once normalized.
fn normalize_name(candidate: &str) -> Result<&str, InvalidName> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good idea! In this case can we use validate_name() instead of validate_named_toolchain() for the previous function?

Comment thread src/toolchain/names.rs

/// Common validate rules for all sorts of toolchain names
fn validate(candidate: &str) -> Result<&str, InvalidName> {
/// Normalization shared by all sorts of toolchain names.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Suggest putting the renaming commit(s) in the beginning of this PR.


Custom toolchain names may contain ASCII letters, ASCII digits, `.`, `_`, and
`-`.
Custom toolchain names may contain any character that [UTS #39] allows in an

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think this commit can be squashed into previous ones to keep the overall diff minimized, as we are not really going to adopt the ASCII-only string pattern for toolchain names.

Comment thread src/toolchain/names.rs
/// because a named toolchain also has to work as a directory name under
/// `.rustup/toolchains`: `:` would open an NTFS alternate data stream, and `'`
/// needs quoting in enough shells to be a nuisance.
const EXCLUDED: &[char] = &[':', '\''];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very carefully designed, nice work :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants