From eab6539ed68e41c71b9e49bafc8eccab7b5b2d48 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sun, 5 Apr 2026 16:14:59 +0900 Subject: [PATCH] codegen: Exclude very recently released version from candidate for latest and omitted versions --- CHANGELOG.md | 8 ++++++++ README.md | 20 +++++++++++++++----- manifests/just.json | 26 ++++++++++++++++++++++++++ manifests/mise.json | 26 ++++++++++++++++++++++++++ tools/codegen/Cargo.toml | 1 + tools/codegen/src/main.rs | 13 ++++++++++--- 6 files changed, 86 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6a3fe8b..280e415e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +- Introduce [dependency cooldown](https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns) when installing with `taiki-e/install-action@`, `tool: @latest`, or `tool: @` to mitigate the risk of supply chain attacks by default. ([#1666](https://github.com/taiki-e/install-action/pull/1666)) + + This action without this cooldown already takes a few hours to a few days for new releases to be reflected (as with other common package managers that verify checksums or signatures), so this should not affect most users. + + See the ["Security" section in readme](https://github.com/taiki-e/install-action#security) for more details. + +- Documentation improvements. + ## [2.72.0] - 2026-04-04 - Support `cargo-xwin`. ([#1659](https://github.com/taiki-e/install-action/pull/1659), thanks @daxpedda) diff --git a/README.md b/README.md index dab67d72..a2055bdb 100644 --- a/README.md +++ b/README.md @@ -100,18 +100,28 @@ See the [development guide](DEVELOPMENT.md) for how to add support for new tool. ## Security -The `@v` and `@` tags are updated with each release. To enhance workflow stability and security against supply chain attacks, use the `@v..` tag or their hash to pin the version. Since all releases are immutable, pinning the version in either way should have the same effect. +The `@v` and `@` tags are updated with each release. If you want to enhance workflow stability and security against supply chain attacks, consider using the `@v..` tag or their hash to pin the version and regularly updating with [dependency cooldown]. Since all releases are immutable, pinning the version in either way should have the same effect. Pinning `@` tags by hash is strongly discouraged, as it causes the workflow to reference a [commit that is not present on the repository](https://docs.zizmor.sh/audits/#impostor-commit) when a new version is released. -When installing the tool from GitHub Releases, the tool version that install-action installs with `tool: @latest` or `tool: @` is associated with the install-action version, so pinning install-action version with the above ways also pins the version of the tool being installed. This also means that if a [dependency cooldown](https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns) applies to the action itself, a cooldown of the same duration or a few days longer will apply to the tools installed by that action. + +### Security on installation from GitHub Releases -### Security on tool installation +**Tools covered in this section:** Tools in the [supported tools list](TOOLS.md) where column "Where will it be installed from" is "GitHub Releases". -When installing the tool from GitHub Releases, this action will download the tool or its installer from GitHub Releases using HTTPS with tlsv1.2+. This is basically considered to be the same level of security as [the recommended installation of rustup](https://www.rust-lang.org/tools/install). +This action will download the tool or its installer from GitHub Releases using HTTPS with tlsv1.2+. This is basically considered to be the same level of security as [the recommended installation of rustup](https://www.rust-lang.org/tools/install). -Additionally, this action will also verify SHA256 checksums for downloaded files in all tools installed from GitHub Releases. This is enabled by default and can be disabled by setting the `checksum` input option to `false` (strongly discouraged to disable). +Additionally, this action will also verify SHA256 checksums for downloaded files for all tools covered in this section. This is enabled by default and can be disabled by setting the `checksum` input option to `false` (strongly discouraged to disable). Additionally, we also verify [artifact attestations](https://docs.github.com/en/actions/concepts/security/artifact-attestations) or signature if the tool publishes artifact attestations or distributes signed archives. Verification is done at the stage of getting the checksum, so disabling the checksum will also disable verification. +When installing with `taiki-e/install-action@`, `tool: `, or `tool: @`, The tool version is reflects upstream releases with a delay of one to a few days (as with other common package managers that verify checksums or signatures). A delay of at least one day is known as [dependency cooldown] and is intended to mitigate the risk of supply chain attacks (the specific cooldown period may be changed in the future). You can bypass the cooldown by explicitly specifying a version. If you want a longer cooldown, consider using the property described below. + +When installing with `tool: ` or `tool: @`, the tool version is associated with the install-action version, so pinning install-action version with the `@v..` tag or their hash also pins the version of the tool being installed. This also means that if a [dependency cooldown] applies to the action itself, a cooldown of one to a few days longer will apply to the tools installed by that action. + +[dependency cooldown]: https://blog.yossarian.net/2025/11/21/We-should-all-be-using-dependency-cooldowns + + +### Security on other installation methods + See the linked documentation for information on security when installed using [snap](https://snapcraft.io/docs) or [cargo-binstall](https://github.com/cargo-bins/cargo-binstall#faq). See the [Supported tools section](#supported-tools) for how to ensure that fallback is not used. diff --git a/manifests/just.json b/manifests/just.json index 0f84b3a8..b037f5eb 100644 --- a/manifests/just.json +++ b/manifests/just.json @@ -27,6 +27,32 @@ "1": { "version": "1.48.1" }, + "1.49.0": { + "x86_64_linux_musl": { + "etag": "0x8DE92D62FFEE2CA", + "hash": "05eb2f068b641b06e5b318796c2e27d4dcca608e65b34329a08c1b9f582611bd" + }, + "x86_64_macos": { + "etag": "0x8DE92D6315E20A9", + "hash": "e0b83a9352952ab25e5cf13f6cb03dd1872416e5d89388b56d6ca58f11b0a3a8" + }, + "x86_64_windows": { + "etag": "0x8DE92D65DAA4399", + "hash": "657338772efd17a31d67285bb5ed691da87741e44311c0366273c6cb7d913b15" + }, + "aarch64_linux_musl": { + "etag": "0x8DE92D633E8329E", + "hash": "993b78f51004248114af22368f69715541542b3c9941c80e02f8ae10eb404ae0" + }, + "aarch64_macos": { + "etag": "0x8DE92D61E96BBDE", + "hash": "d21b20df01ec9b9762b0ef08e56ae8dccf3738770edeafa8d2b3a750aee06d78" + }, + "aarch64_windows": { + "etag": "0x8DE92D65E1C5B47", + "hash": "e73cd7b3c4fb363f703f99caaa71d4ab114a92205b2ef313212f3b2085d3ee64" + } + }, "1.48": { "version": "1.48.1" }, diff --git a/manifests/mise.json b/manifests/mise.json index ca38c145..19c7f18e 100644 --- a/manifests/mise.json +++ b/manifests/mise.json @@ -36,6 +36,32 @@ "2026.4": { "version": "2026.4.3" }, + "2026.4.4": { + "x86_64_linux_musl": { + "etag": "0x8DE92B8CD42D7A7", + "hash": "3d746d5a137e63ed88e4e2213a9706a43dff68c995376fabb753a90ec799af18" + }, + "x86_64_macos": { + "etag": "0x8DE92B8CF701697", + "hash": "e3fe092643f3e68c3a2be9eef217937f241accb81891332678550c15224627c0" + }, + "x86_64_windows": { + "etag": "0x8DE92B8D03C1E31", + "hash": "5c879723f693514f6f8f725270724561cd8ebdf913188839ad1f879900bf5719" + }, + "aarch64_linux_musl": { + "etag": "0x8DE92B8CA044A7E", + "hash": "e2da1fd598b4aa347761daf373e51608cbc4465c6b076b60b22cb1ef659cf97e" + }, + "aarch64_macos": { + "etag": "0x8DE92B8CEAE639C", + "hash": "22459d2852b716f5dbbdb07173ae0fb28d99b701589f6f6a73d85ac47a497de8" + }, + "aarch64_windows": { + "etag": "0x8DE92B8D051630C", + "hash": "6cada4eeeef1a9cd074962a76fa9a6f107b5b791b01839fc3355d3f3b1604c85" + } + }, "2026.4.3": { "x86_64_linux_musl": { "etag": "0x8DE918787D0FCD3", diff --git a/tools/codegen/Cargo.toml b/tools/codegen/Cargo.toml index 430da4e9..e746db38 100644 --- a/tools/codegen/Cargo.toml +++ b/tools/codegen/Cargo.toml @@ -8,6 +8,7 @@ install-action-manifest-schema = { path = "../manifest-schema" } anyhow = "1" flate2 = "1" fs-err = "3" +jiff = { version = "0.2", default-features = false, features = ["std", "serde"] } minisign-verify = "0.2" ring = "0.17" semver = { version = "1", features = ["serde"] } diff --git a/tools/codegen/src/main.rs b/tools/codegen/src/main.rs index 0d19996a..71ef81ff 100644 --- a/tools/codegen/src/main.rs +++ b/tools/codegen/src/main.rs @@ -23,6 +23,8 @@ use install_action_internal_codegen::{ use serde::de::DeserializeOwned; use spdx::expression::{ExprNode, ExpressionReq, Operator}; +const DEFAULT_COOLDOWN: u64 = 24; + fn main() { let args: Vec<_> = env::args().skip(1).collect(); if args.is_empty() || args.iter().any(|arg| arg.starts_with('-')) { @@ -59,6 +61,7 @@ fn main() { eprintln!("downloading metadata from {GITHUB_API_START}repos/{repo}"); let repo_info: github::RepoMetadata = download_json(&format!("{GITHUB_API_START}repos/{repo}")); + let before = jiff::Timestamp::now() - Duration::from_hours(DEFAULT_COOLDOWN); eprintln!("downloading releases from {GITHUB_API_START}repos/{repo}/releases"); let mut releases: github::Releases = vec![]; // GitHub API returns up to 100 results at a time. If the number of releases @@ -772,17 +775,20 @@ fn main() { } else if !semver_versions.is_empty() { let mut prev_version = semver_versions.iter().next().unwrap(); for version in &semver_versions { + if releases[&Reverse(version.clone())].1.published_at > before { + continue; // Exclude very recently released version from candidate for latest and omitted versions. + } if let Some(crates_io_info) = &crates_io_info { if let Some(v) = crates_io_info.versions.iter().find(|v| v.num == *version) { if v.yanked { - continue; // Exclude yanked version from candidate for "latest". + continue; // Exclude yanked version from candidate for latest and omitted versions. } } else { - continue; // Exclude version not released on crates.io from candidate for "latest". + continue; // Exclude version not released on crates.io from candidate for latest and omitted versions. } } if base_info.broken.contains(version) { - continue; // Exclude version marked as broken from candidate for "latest". + continue; // Exclude version marked as broken from candidate for latest and omitted versions. } if !(version.major == 0 && version.minor == 0) { manifests.map.insert( @@ -1249,6 +1255,7 @@ mod github { pub(crate) struct Release { pub(crate) tag_name: String, pub(crate) prerelease: bool, + pub(crate) published_at: jiff::Timestamp, pub(crate) assets: Vec, }