Skip to content

Commit 7d790f7

Browse files
authored
Merge pull request #460 from OriginTrail/security/supply-chain-hardening
security(ci): harden supply chain against TeamPCP-class attacks
2 parents 23783f9 + 808ff0f commit 7d790f7

19 files changed

Lines changed: 2727 additions & 134 deletions

.github/CODEOWNERS

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# CODEOWNERS — required reviewer routing for security-sensitive paths.
2+
#
3+
# Branch protection on `main` / `v10-rc` should have "Require review from
4+
# Code Owners" enabled (admin task — see
5+
# docs/security/SUPPLY_CHAIN_HARDENING.md §C). With that toggle on, any
6+
# PR that touches a path listed below requires an approving review from
7+
# at least one of the listed owners before it can merge. Without
8+
# CODEOWNERS, the "code-owner review" branch protection rule is a no-op:
9+
# there is nobody to ask, so the requirement is silently satisfied for
10+
# every PR. The 2024 reviewdog supply-chain compromise hinged on this
11+
# exact failure mode — an attacker landed a workflow change that no
12+
# named maintainer was required to approve.
13+
#
14+
# Owner syntax: GitHub @-usernames or @org/team names. Teams are
15+
# preferred for shared infra (no churn when the active maintainer
16+
# rotates). Individual handles are fine for the bootstrap period below
17+
# and can be migrated to a team mention later without touching the
18+
# patterns.
19+
#
20+
# Bootstrap owners — derived from the most recent committers on these
21+
# paths at the time CODEOWNERS landed. Replace with `@OriginTrail/<team>`
22+
# once a maintainers' team is created in the org. Each path needs at
23+
# LEAST two owners so no single account compromise can self-approve a
24+
# malicious change here.
25+
26+
# ----------------------------------------------------------------------
27+
# CI workflow files — full attacker surface. Anything that runs on a
28+
# privileged event (`push` to main, `pull_request_target`, `schedule`,
29+
# `workflow_dispatch`) inherits the workflow's `permissions:` block,
30+
# so a malicious workflow change is equivalent to handing out a token
31+
# in the worst case. These MUST get a security-aware review.
32+
.github/ @branarakic @Jurij89 @zsculac
33+
.github/workflows/ @branarakic @Jurij89 @zsculac
34+
.github/actions/ @branarakic @Jurij89 @zsculac
35+
.github/dependabot.yml @branarakic @Jurij89 @zsculac
36+
.github/zizmor.yml @branarakic @Jurij89 @zsculac
37+
.github/CODEOWNERS @branarakic @Jurij89 @zsculac
38+
39+
# ----------------------------------------------------------------------
40+
# Publish credentials surface — anything that influences what gets
41+
# packed, published, or which packages are public. A change that flips
42+
# a `"private": true` to false on a previously-private package, or
43+
# that adds a postinstall script, lands here.
44+
package.json @branarakic @Jurij89 @zsculac
45+
pnpm-lock.yaml @branarakic @Jurij89 @zsculac
46+
pnpm-workspace.yaml @branarakic @Jurij89 @zsculac
47+
.npmrc @branarakic @Jurij89 @zsculac
48+
packages/*/package.json @branarakic @Jurij89 @zsculac
49+
packages/*/.npmrc @branarakic @Jurij89 @zsculac
50+
51+
# ----------------------------------------------------------------------
52+
# Release / deploy scripts — covered as code, not config. A modification
53+
# here can change what `pnpm build` produces or which file the release
54+
# workflow uploads as a release asset, both of which are publish-trust
55+
# boundaries.
56+
scripts/ @branarakic @Jurij89 @zsculac
57+
packages/cli/scripts/ @branarakic @Jurij89 @zsculac
58+
59+
# ----------------------------------------------------------------------
60+
# Security documentation — keeps the threat-model description in sync
61+
# with the controls actually configured in this repo. Drift between
62+
# the doc and the configured controls was the gap that PR review found
63+
# the first time around.
64+
docs/security/ @branarakic @Jurij89 @zsculac
65+
SECURITY.md @branarakic @Jurij89 @zsculac

.github/dependabot.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Dependabot configuration — keeps the supply chain on a maintained surface.
2+
#
3+
# Two ecosystems matter for the TeamPCP-class attack model:
4+
#
5+
# 1. `github-actions` — every `uses:` we run executes attacker-supplied
6+
# code on a runner with our `GITHUB_TOKEN` (and, on main, NPM_TOKEN).
7+
# Pinned to full commit SHAs in every workflow; this updater bumps
8+
# each SHA when upstream cuts a new release. PRs include the release
9+
# notes inline so a reviewer can spot suspicious changes.
10+
#
11+
# 2. `npm` — same reasoning, applied to runtime dependencies. The
12+
# CanisterWorm wave proved that even narrow-scope npm publishers get
13+
# compromised; the only cheap defence is a fast lockfile bump cadence
14+
# so we land patched versions before a 0-day in our tree sits idle.
15+
#
16+
# Both run weekly to minimise PR noise. Updates land as PRs and follow
17+
# normal review + CI gates. Major version bumps for runtime npm deps are
18+
# grouped per-package-manager to avoid one-PR-per-package floods.
19+
20+
version: 2
21+
updates:
22+
- package-ecosystem: github-actions
23+
directory: /
24+
schedule:
25+
interval: weekly
26+
day: monday
27+
time: '06:00'
28+
timezone: 'Europe/Belgrade'
29+
open-pull-requests-limit: 10
30+
labels:
31+
- dependencies
32+
- github-actions
33+
- security
34+
commit-message:
35+
prefix: 'ci'
36+
include: scope
37+
groups:
38+
# Group all `actions/*` first-party updates into one PR per week —
39+
# they almost always release in lockstep and reviewing them as a
40+
# bundle is the right ergonomics.
41+
actions-core:
42+
patterns:
43+
- 'actions/*'
44+
# Third-party action updates are higher-risk; keep each one in its
45+
# own PR so the upgrade can be reviewed in isolation.
46+
47+
- package-ecosystem: npm
48+
directory: /
49+
schedule:
50+
interval: weekly
51+
day: monday
52+
time: '06:00'
53+
timezone: 'Europe/Belgrade'
54+
open-pull-requests-limit: 15
55+
versioning-strategy: increase-if-necessary
56+
labels:
57+
- dependencies
58+
- npm
59+
- security
60+
commit-message:
61+
prefix: 'chore(deps)'
62+
include: scope
63+
groups:
64+
# Group dev/build-tool updates — eslint, prettier, typescript,
65+
# vitest, vite, playwright, types/*. Bumping these as a bundle keeps
66+
# PR review noise manageable; runtime risk is low.
67+
dev-tooling:
68+
dependency-type: development
69+
patterns:
70+
- 'eslint*'
71+
- 'prettier*'
72+
- 'typescript'
73+
- 'vitest'
74+
- '@vitest/*'
75+
- 'vite'
76+
- '@vitejs/*'
77+
- '@playwright/*'
78+
- 'playwright'
79+
- '@types/*'
80+
- 'tsx'
81+
- 'ts-node'
82+
# Group hardhat ecosystem — also low-risk and updates in lockstep.
83+
hardhat:
84+
patterns:
85+
- 'hardhat'
86+
- '@nomicfoundation/*'
87+
- 'hardhat-*'
88+
# Group transitive-only updates into a single weekly PR. These are
89+
# packages we do not import directly but our direct deps drag in
90+
# via the lockfile. Without this group every transitive bump opens
91+
# its own PR, which used to flood the queue and was the reason an
92+
# earlier revision of this file applied `allow: dependency-type:
93+
# direct` — that filter, however, also blocked transitive
94+
# *security* fixes from showing up as normal weekly PRs and
95+
# silently relied on Dependabot security alerts to catch them.
96+
# Grouping is the structurally correct fix: noise goes away, but
97+
# transitive-only CVE patches still reach reviewers within a
98+
# week of upstream landing them.
99+
transitive:
100+
dependency-type: production
101+
update-types:
102+
- 'minor'
103+
- 'patch'
104+
patterns:
105+
- '*'
106+
# `dev-tooling` and `hardhat` above declare their own patterns,
107+
# so they take precedence and this catch-all only grabs what
108+
# they don't claim.
109+
# Runtime/production direct deps NOT in the groups above (ethers,
110+
# n3, commander, better-sqlite3, react, …) each get their own PR
111+
# so any behaviour change is reviewable in isolation.
112+
#
113+
# Intentionally NO `allow: dependency-type: direct` filter — see
114+
# the `transitive` group above. The earlier filter is the gap
115+
# PR460-02 / P0 #2 flagged in the security review.

0 commit comments

Comments
 (0)