Skip to content

Verify downloaded Terraform zip against published SHA256SUMS / SHA256SUMS.sig #556

Description

@potiuk

Hi! This is a security-hygiene request from a downstream consumer — the Apache Software Foundation's allowlist of approved GitHub Actions.

setup-terraform currently downloads the Terraform release zip and extracts it without verifying its integrity:

async function downloadCLI (url) {
core.debug(`Downloading Terraform CLI from ${url}`);
const pathToCLIZip = await tc.downloadTool(url);
let pathToCLI = '';
core.debug('Extracting Terraform CLI zip file');
if (os.platform().startsWith('win')) {
core.debug(`Terraform CLI Download Path is ${pathToCLIZip}`);
const fixedPathToCLIZip = `${pathToCLIZip}.zip`;
io.mv(pathToCLIZip, fixedPathToCLIZip);
core.debug(`Moved download to ${fixedPathToCLIZip}`);
pathToCLI = await tc.extractZip(fixedPathToCLIZip);
} else {
pathToCLI = await tc.extractZip(pathToCLIZip);
}
core.debug(`Terraform CLI path is ${pathToCLI}.`);
if (!pathToCLIZip || !pathToCLI) {
throw new Error(`Unable to download Terraform from ${url}`);
}
return pathToCLI;
}

async function downloadCLI (url) {
  core.debug(`Downloading Terraform CLI from ${url}`);
  const pathToCLIZip = await tc.downloadTool(url);
  ...
  pathToCLI = await tc.extractZip(pathToCLIZip);
  ...
}

TLS protects the bytes in transit from releases.hashicorp.com, but the action takes no defense-in-depth step beyond that — a compromised mirror, CDN incident, or any future change to the resolved URL would land an arbitrary binary on every runner that uses the action.

HashiCorp already publishes everything needed to close that gap at each release:

  • terraform_<version>_SHA256SUMS — SHA256 of every release asset
  • terraform_<version>_SHA256SUMS.<keyID>.sig — detached PGP signature over the sums file, signed with the HashiCorp release GPG key

Proposed fix

A minimal fix inside downloadCLI:

  1. After tc.downloadTool(url) returns the zip path, also fetch terraform_<v>_SHA256SUMS for the same release.
  2. Find the line for the platform-specific zip filename.
  3. Hash the downloaded zip with Node's crypto.createHash('sha256') and compare; fail loudly if it doesn't match.

A stronger fix additionally verifies SHA256SUMS.sig against the HashiCorp public key — @hashicorp/js-releases already knows about the release index, so the signature path is a natural extension.

Downstream impact

The ASF reviews every new commit hash of every action it adds to its allowlist. Our verification script flags every release of setup-terraform because of this missing check — most recently on the v4.0.0 → v4.0.1 bump in apache/infrastructure-actions#836. We're approving 836 because the download shape didn't change between v4.0.0 (already approved) and v4.0.1, but every future bump will need a manual security re-review until the action verifies what it downloads.

Happy to send a PR if it'd help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions