Skip to content

Commit 9278281

Browse files
committed
fix(ci): reject unknown job names in github-custom-job-secrets
1 parent e179c0c commit 9278281

4 files changed

Lines changed: 62 additions & 12 deletions

File tree

book/src/reference/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,7 @@ Allows you to customize the secrets passed to your custom CI jobs.
14911491
14921492
Jobs not present in this map keep the legacy `secrets: inherit` behavior.
14931493
If a configured job has an empty list/map, `secrets` is omitted entirely for that job.
1494+
Unknown job names in this map are an error.
14941495
14951496
You can use either:
14961497

cargo-dist/src/backend/ci/github.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ impl GithubCiInfo {
296296
let mut job_permissions = ci_config.permissions.clone();
297297
let job_secrets = ci_config.secrets.clone();
298298
let configured_secret_jobs = &ci_config.configured_secret_jobs;
299+
let known_custom_jobs = collect_custom_job_names(ci_config);
300+
validate_job_secret_config_keys(configured_secret_jobs, &known_custom_jobs)?;
299301
// user publish jobs default to elevated privileges
300302
for JobStyle::User(name) in &ci_config.publish_jobs {
301303
job_permissions.entry(name.clone()).or_insert_with(|| {
@@ -623,6 +625,41 @@ fn build_jobs(
623625
Ok(output)
624626
}
625627

628+
fn collect_custom_job_names(ci_config: &GithubCiConfig) -> SortedSet<String> {
629+
ci_config
630+
.plan_jobs
631+
.iter()
632+
.chain(ci_config.build_local_jobs.iter())
633+
.chain(ci_config.build_global_jobs.iter())
634+
.chain(ci_config.host_jobs.iter())
635+
.chain(ci_config.publish_jobs.iter())
636+
.chain(ci_config.post_announce_jobs.iter())
637+
.filter_map(|job| match job {
638+
JobStyle::User(name) => Some(name.clone()),
639+
})
640+
.collect()
641+
}
642+
643+
fn validate_job_secret_config_keys(
644+
configured_secret_jobs: &SortedSet<String>,
645+
known_custom_jobs: &SortedSet<String>,
646+
) -> DistResult<()> {
647+
let unknown_jobs = configured_secret_jobs
648+
.iter()
649+
.filter(|job_name| !known_custom_jobs.contains(*job_name))
650+
.cloned()
651+
.collect::<Vec<_>>();
652+
653+
if unknown_jobs.is_empty() {
654+
return Ok(());
655+
}
656+
657+
Err(DistError::GithubUnknownCustomJobSecrets {
658+
jobs: unknown_jobs,
659+
known_jobs: known_custom_jobs.iter().cloned().collect(),
660+
})
661+
}
662+
626663
/// Get the best `cache-provider` key to use for <https://github.com/Swatinem/rust-cache>.
627664
///
628665
/// In the future we might make "None" here be a way to say "disable the cache".
@@ -1083,19 +1120,18 @@ mod tests {
10831120
use super::*;
10841121

10851122
#[test]
1086-
fn build_jobs_ignores_unknown_secret_config_keys() {
1087-
let jobs = vec![JobStyle::User("known-job".to_owned())];
1088-
let perms = SortedMap::new();
1089-
let secrets = SortedMap::from_iter([(
1090-
"unknown-job".to_owned(),
1091-
SortedMap::from_iter([("TOKEN".to_owned(), "TOKEN".to_owned())]),
1092-
)]);
1123+
fn validate_job_secret_config_keys_rejects_unknown_jobs() {
10931124
let configured_secret_jobs = SortedSet::from_iter(["unknown-job".to_owned()]);
1125+
let known_custom_jobs = SortedSet::from_iter(["known-job".to_owned()]);
10941126

1095-
let built = build_jobs(&jobs, &perms, &secrets, &configured_secret_jobs).unwrap();
1096-
assert_eq!(built.len(), 1);
1097-
assert_eq!(built[0].name, "known-job");
1098-
assert!(built[0].secrets.is_none());
1127+
let err = validate_job_secret_config_keys(&configured_secret_jobs, &known_custom_jobs)
1128+
.expect_err("unknown job keys should fail closed");
1129+
1130+
let DistError::GithubUnknownCustomJobSecrets { jobs, known_jobs } = err else {
1131+
panic!("expected GithubUnknownCustomJobSecrets");
1132+
};
1133+
assert_eq!(jobs, vec!["unknown-job".to_owned()]);
1134+
assert_eq!(known_jobs, vec!["known-job".to_owned()]);
10991135
}
11001136

11011137
#[test]

cargo-dist/src/errors.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,20 @@ pub enum DistError {
465465
levels: Vec<String>,
466466
},
467467

468+
/// Unknown custom job key specified in GitHub custom job secrets config
469+
#[error(
470+
"One or more github-custom-job-secrets entries didn't match configured custom jobs: {jobs:?}"
471+
)]
472+
#[diagnostic(help(
473+
"Configured custom jobs are: {known_jobs:?}. Make sure each key matches a job name (without leading './') from plan-jobs, local-artifacts-jobs, global-artifacts-jobs, host-jobs, publish-jobs, or post-announce-jobs."
474+
))]
475+
GithubUnknownCustomJobSecrets {
476+
/// Unknown custom job keys from github-custom-job-secrets
477+
jobs: Vec<String>,
478+
/// Known custom job names from configured job lists
479+
known_jobs: Vec<String>,
480+
},
481+
468482
/// An unknown target was found
469483
#[error("Unrecognized target: {target}")]
470484
#[diagnostic(help("The full list of supported targets can be found here: https://axodotdev.github.io/cargo-dist/book/reference/config.html#targets"))]

cargo-dist/tests/integration-tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,6 @@ plan-list = ["PLAN_TOKEN"]
589589
local-empty-list = []
590590
host-list-multi = ["CLOUDFLARE_API_TOKEN", "DEPLOY_KEY"]
591591
post-list = ["POST_TOKEN"]
592-
unknown-job = ["SHOULD_BE_IGNORED"]
593592
594593
[workspace.metadata.dist.github-custom-job-secrets.local-map]
595594
DEPLOY_TOKEN = "ORG_DEPLOY_TOKEN"

0 commit comments

Comments
 (0)