Skip to content

Implement PUT /api/v1/trusted_publishing/github_configs API endpoint #11113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

Turbo87
Copy link
Member

@Turbo87 Turbo87 commented May 5, 2025

This PR implements the first API endpoint for "Trusted Publishing" support in crates.io.

PUT /api/v1/trusted_publishing/github_configs can be used to create Trusted Publishing configurations specific to GitHub Actions. As discussed in #11062 (comment), it seems best to make the configurations provider-specific, and only have the token exchange endpoint be generic.

The endpoint is currently limited to cookie authentication, so that we don't have to commit to this API yet, since our frontend will be the only user. Once we have validated that we are happy with the API we can open it up to API token authentication, but that will likely mean introducing another token scope, which seems out of scope for this PR.

This PR is probably best reviewed commit by commit.

Related:

@Turbo87 Turbo87 added C-enhancement ✨ Category: Adding new behavior or a change to the way an existing feature works A-backend ⚙️ labels May 5, 2025
@Turbo87 Turbo87 requested a review from a team May 5, 2025 17:14
Copy link
Contributor

@eth3lbert eth3lbert left a comment

Choose a reason for hiding this comment

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

I haven't looked at all the other commits yet, but here's some feedback on the GitHub owner and repository validation parts (a bit more hardening).

Comment on lines +43 to +51
static RE_VALID_GITHUB_OWNER: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r"^[a-zA-Z0-9][a-zA-Z0-9-]*$").unwrap());

if owner.is_empty() {
Err(ValidationError::OwnerEmpty)
} else if owner.len() > MAX_FIELD_LENGTH {
Err(ValidationError::OwnerTooLong)
} else if !RE_VALID_GITHUB_OWNER.is_match(owner) {
Err(ValidationError::OwnerInvalid)
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem to cover the following cases:

  1. Ends with -
  2. Length exceeds 39

Comment on lines +58 to +66
static RE_VALID_GITHUB_REPO: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r"^[a-zA-Z0-9-_.]+$").unwrap());

if repo.is_empty() {
Err(ValidationError::RepoEmpty)
} else if repo.len() > MAX_FIELD_LENGTH {
Err(ValidationError::RepoTooLong)
} else if !RE_VALID_GITHUB_REPO.is_match(repo) {
Err(ValidationError::RepoInvalid)
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't seem to cover the following cases:

  1. Reserved names . and ..
  2. Length exceeds 100

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the currently proposed regex might be sufficient, as the regex crate doesn't support lookaround features. If we'd like to harden this part, although there might be some workarounds for lookaround, I think it would be best implemented with simple string-related checks for maintenance reasons.

Copy link
Contributor

@LawnGnome LawnGnome left a comment

Choose a reason for hiding this comment

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

I think @eth3lbert stole my thunder on the only actual things I was going to ask about, so otherwise: LGTM. ⚡


pub fn validate_environment(env: &str) -> Result<(), ValidationError> {
static RE_INVALID_ENVIRONMENT_CHARS: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r#"[\x00-\x1F\x7F'"`,;\\]"#).unwrap());
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm betting you had fun figuring out what "non-printable character" meant in this context (from the GitHub error).

Comment on lines +36 to +47
crates.io user {user} has added a new \"Trusted Publishing\" configuration for GitHub Actions to a crate that you manage ({krate}). Trusted publishers act as trusted users and can publish new versions of the crate automatically.

Trusted Publishing configuration:

- Repository owner: {repository_owner}
- Repository name: {repository_name}
- Workflow filename: {workflow_filename}
- Environment: {environment}

If you did not make this change and you think it was made maliciously, you can remove the configuration from the crate via the \"Settings\" tab on the crate's page.

If you are unable to revert the change and need to do so, you can email [email protected] to communicate with the crates.io support team."
Copy link
Contributor

Choose a reason for hiding this comment

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

This is fine for now, but we should remember to link this to the documentation we write when this actually gets released.

@Turbo87
Copy link
Member Author

Turbo87 commented May 7, 2025

@eth3lbert @LawnGnome probably should've mentioned that in the PR message: the validation rules and email template are more or less taken directly from the PyPI implementation 😅 (@woodruffw please let me know if I need to adjust anything to comply with licensing etc.)

I guess technically we don't need the validations at all, since we only need to compare the values with the claims from the OIDC token. They are however useful for:

  • avoiding GitHub API calls for user names that are known to be invalid
  • avoiding common user mistakes when entering these values

I don't think we need to be quite as strict as GitHub though, so now I'm wondering why PyPI implemented e.g. the non-printable characters validation 🤔

@Turbo87 Turbo87 force-pushed the trustpub-create-config branch from 2d84763 to 2585f04 Compare May 7, 2025 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-backend ⚙️ C-enhancement ✨ Category: Adding new behavior or a change to the way an existing feature works
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants