Skip to content
Merged
14 changes: 6 additions & 8 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
permissions:
contents: read

env:
FORCE_COLOR: 2

jobs:
build:
runs-on: ubuntu-latest
Expand All @@ -21,17 +24,12 @@ jobs:
node-version: ['20', '22']

steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the benefit of pinning to a commit hash for first-party packages published by GitHub themselves?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there is such a distinction between first-party and third-party actions, in the sense that the checkout action, and all other GitHub official actions, are following the same development model as any other action: they have a public GH repository (https://github.com/actions/checkout), that is open to external contributors, and therefore the threat model is the same for this action as any other one.

They are not immune to a repository takeover that would push or rewrite an existing tag, and I would argue that given the ubiquity of this particular checkout action, it is even more important to pin them.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I was wondering becuase I regularly hear the opposite advice: never pin to a commit hash, because the commit hash could come from a fork of the repository (which is a well known GitHub quirk
[better source needed])

For example, a malicious contributor could change this line to the following:

Suggested change
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/checkout@5a96951a20872f15b767339256a01fe5c4d1478f # v4.123.123

And for a code reviewer, it's a lot of work to manually check if the commit hash matches the version in the code comment. So the code reviers might not bother to check, since "actions/checkout" looks legitimate.

In that example above, commit 5a9695 comes from a deleted fork, so it could contain any malicious code.

...and GitHub Actions will happily pull the malicious commit from the fork and run it, ignoring the explicit repository name of "actions/checkout":
image


anyway.... this is all very hypothetical, so don't read this as a code review, I'm just curious about the rationale behind this change

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But actions/checkout is actually maintained by Github, right? At least the COC suggests so. TBH I have a higher trust that Github has their merges under control … or resolves them quickly than any other dependency we have in iD. Which is why I would vote to keep this simple and follow usage recommendations they provide https://github.com/actions/checkout?tab=readme-ov-file#usage

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@k-yle: manual updates of Actions SHA should be prohibited and only PRs opened by Dependabot should be reviewed and merged, especially since you already have Dependabot configured for this repository.

Even GitHub's own Secure Reference manual recommend to pin the SHA of third party Actions: https://docs.github.com/en/actions/reference/security/secure-use#using-third-party-actions

@tordans: it is indeed maintained by GitHub, but the question is, why should you treat Actions developed by GitHub any differently than any other Action ? From your perspective, they are part of your supply chain, and you use them to deploy staging instances and build your code in your workflows.

I would argue that, given their market size, and give the amount of repos using these basic Actions (checkout, setup, ...), it makes them a way more interesting target than any other third party Action. You might expect (and this could be the case in practice) that they have additional security measures and processes in place for these repositories, but their employees and code owners are still vulnerable to phishing, or any other type of attack that you could read about in other supply chain attacks in the past few years.

Regarding the argument of speed of resolution, you might be right, but if I look at the iD repository statistics of your workflows (https://github.com/openstreetmap/iD/actions/metrics/usage?dateRangeType=DATE_RANGE_TYPE_CURRENT_WEEK), the build job ran on average 10 times per day for the last week, and the deploy to staging instance 3.5 times per day, which in my opinion leaves plenty of time to an attacker that compromised a tagged Action to run its code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to point out that you already have a very good best practice in place (#9273) by reducing the GITHUB_TOKEN default permissions, very positive !

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i find this topic quite interesting, because this approach basically moves the onus to the downstream maintainer to manually enforce your rule that "manual updates of Actions SHA should be prohibited"

and the likelihood of someone forgetting this rule is significantly higher than the likelihood of github's own repository getting compromised.

which is why i'm quite suprised that github's advice is the opposite of defensive design. There's also a mistake on that docs page which affects the viability of this solution. The docs proclaim:

Dependabot only creates alerts for vulnerable actions that use semantic versioning and will not create alerts for actions pinned to SHA values. [source]

But this contradicts dependabot/dependabot-core#5951 😆 suggesting that (at least part of) those docs are 3 years out-of-date

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm failing a bit to see how it's opposing the defensive design paradigm, could you expand on this ? To be noted that pinning hashes is also recommended by the OpenSSF in their Scorecard approach, with the same rationale, and the same logic to let the dependencies be updated by automated means: https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PS: I've now applied the version/hash pinning also to the other two workflows (codespell and transifex) that we use in this repo.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, I realised I never replied to this discussion 😅 For completeness, I meant that pinning to a commit hash requires maintainers to do extra work1, with no safeguards to ensure that the hashes are actually checked.

Generally speaking, requiring people to remember and follow an SOP isn't great defensive design, and in other industries this would be one the weakest types of defenses.


So it's easy for a malicious user to fool a maintainer into accepting code that looks legitimate, such as this line:

- uses: actions/checkout@85b8272e24166014adcf9ff6ef3701031878ec80 # v5.0.0

It includes the words actions/checkout, so many people would assume that this code must come from the actions/checkout repo. but it doesn't!

if we open that commit (actions/checkout@85b8272) the author and content match the real v5 commit, the purported author is a github employee, and the URL contains actions/checkout. So it looks very legitimate.
There is a warning banner, but it only explains the problem, not the risk. And unfortunately this banner is understandably oft ignored because of several known false-positives.

In reality, 85b8272 is not from the actions/checkout repo. It's from a deleted fork, and contains malicious code added several commit ago, with fake commit history replayed ontop of the malicious commit.


So anyway, this approach works for us, but it's shockingly easy to fool people when you use commit hashes

Footnotes

  1. (manually validating every commit hash added by external contributors)

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node-version }}
- run: npm clean-install
env:
FORCE_COLOR: 2
- run: npm audit signatures
- run: npm run all
env:
FORCE_COLOR: 2
- run: npm run test
env:
FORCE_COLOR: 2
4 changes: 2 additions & 2 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ jobs:
name: Check for spelling errors
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: codespell-project/actions-codespell@v2.1
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: codespell-project/actions-codespell@406322ec52dd7b488e48c1c4b82e2a8b3a1bf630 # v2.1
with:
check_filenames: true
skip: ./.git,./data/territory_languages.json,./data/imagery.json,./data/languages.json,./data/address_formats.json,./dist/locales,./docs/img,./dist/img,./css,package.json,package-lock.json,scripts,docs
Expand Down
12 changes: 8 additions & 4 deletions .github/workflows/staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,32 @@ jobs:
runs-on: ubuntu-latest
environment: workflows
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/setup-node@v4.4.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: '.nvmrc'
# install and build development version of id-tagging-schema
- uses: actions/checkout@v4.2.2
- name: Checkout the id-tagging-schema repository code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: openstreetmap/id-tagging-schema
path: './id-tagging-schema'
- run: npm clean-install
working-directory: './id-tagging-schema'
- run: npm audit signatures
- run: npm run translations
working-directory: './id-tagging-schema'
env:
transifex_password: ${{secrets.TX_TOKEN}}
if: env.transifex_password != null
- run: npm run dist
working-directory: './id-tagging-schema'
- run: mkdir dist/id-tagging-schema && mv id-tagging-schema/dist dist/id-tagging-schema/dist
- name: id-tagging-schema - Create directories
run: mkdir dist/id-tagging-schema && mv id-tagging-schema/dist dist/id-tagging-schema/dist
# build iD using freshest version of presets and ELI
- run: npm clean-install
- run: npm install editor-layer-index
- run: npm audit signatures
- run: mkdir dist/data
- run: npm run imagery
- run: npm run all
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/transifex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
environment: workflows

steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Transifex client
run: |
curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash
Expand Down