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:
- Lists all usages of
tj-actions/changed-files
- Prioritises public repositories
- Finds all usages which aren't pinning to a digest
- Finds all usages which are pinning to a known bad digest (
0e58ed8
)
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 |