Responding to the `tj-actions/changed-files` supply chain attack

How Dependency Management Data can be used to determine where the tj-actions/changed-files GitHub Action could be compromised in your organisation.

Context

As noted in blog posts by Semgrep and Step Security, a malicious payload was introduced in the tj-actions/changed-files GitHub Action.

This impacts usages of the Action where the digest is not pinned and instead relies on the (mutable) tag:

- name: Get changed files
  id: changed-files
  uses: tj-actions/changed-files@v44

Additionally, if a user was pinning (as per best practices) but then updated to the known malicious digest, this would also impact them.

Aside: as of DMD v0.116.2, there is now an advisory that flags unpinned digests.

Problem

We need to write a query that:

Data

Let's say that we have the following data:

In the renovate table:

platform organisation repo package_name version current_version digest package_manager package_file_path dep_types package_type
github govuk-one-login authentication-api tj-actions/changed-files v45.0.3 v45.0.3 c3a1bb2c992d77180ae65be6ae6c166cf40f857c github-actions .github/workflows/pre-merge-checks-gha.yml ["action"] github
github some-org some-repo tj-actions/changed-files v45.0.3 v45.0.3 github-actions .github/workflows/pre-merge-checks-gha.yml ["action"] github
github some-org another-repo tj-actions/changed-files 0e58ed8671d6b60d0890c21b07f8835ace038e67 0e58ed8671d6b60d0890c21b07f8835ace038e67 0e58ed8671d6b60d0890c21b07f8835ace038e67 github-actions .github/workflows/more-pre-merge-checks-gha.yml ["action"] github

And in the sboms table:

component_name package_name version current_version package_type
some-sbom tj-actions/changed-files v44 v44 github

And in the repository_metadata table:

platform organisation repo is_monorepo is_fork repository_type repository_usage visibility description additional_metadata
github govuk-one-login authentication-api FALSE FALSE service PUBLIC
github some-org some-repo FALSE FALSE unknown PRIVATE
github some-org another-repo FALSE FALSE unknown PUBLIC

Query

To solve this problem, we could then run:

select
renovate.platform,
renovate.organisation,
renovate.repo,
'' as component_name,
visibility,
package_file_path,
version,
digest
from
  renovate left join repository_metadata
  on  renovate.platform = repository_metadata.platform
  and renovate.organisation = repository_metadata.organisation
  and renovate.repo = repository_metadata.repo
where
  -- get the affected package
  package_name = 'tj-actions/changed-files'
  AND
  (
    -- unpinned
    digest is null
    or
    -- pinned to known bad commit
    digest = '0e58ed8671d6b60d0890c21b07f8835ace038e67'
  )
union
select
component_metadata.platform,
component_metadata.organisation,
component_metadata.repo,
sboms.component_name,
visibility,
-- as we don't get this from SBOMs
'' as package_file_path,
version,
-- as we don't get this data in SBOMs
null as digest
from
  sboms
  left join component_metadata
  on  sboms.component_name = component_metadata.component_name
  left join repository_metadata
  on  component_metadata.platform = repository_metadata.platform
  and component_metadata.organisation = repository_metadata.organisation
  and component_metadata.repo = repository_metadata.repo
where
  -- get the affected package
  package_name = 'tj-actions/changed-files'
  AND
  (
    -- as we can't get access to the digests, we can instead find when there's a version
    version like 'v%'
    or
    -- pinned to known bad commit
    version = '0e58ed8671d6b60d0890c21b07f8835ace038e67'
  )

Based on the above data, we get the following data:

platform organisation repo component_name visibility package_file_path version digest
github some-org some-repo PRIVATE .github/workflows/pre-merge-checks-gha.yml v45.0.3
github some-org another-repo PUBLIC .github/workflows/more-pre-merge-checks-gha.yml 0e58ed8671d6b60d0890c21b07f8835ace038e67 0e58ed8671d6b60d0890c21b07f8835ace038e67
some-sbom v44 v44