Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 80 additions & 4 deletions README-cs.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,34 @@ Pozor na jedno omezenΓ­: **`renovate-config-validator` nevaliduje nΓ‘zvy
managerΕ―.** `matchManagers: ["npm", "pnpm", "yarn"]` projde čistΔ› a nematchuje
nic. Gate pokrΓ½vΓ‘ schΓ©ma, ne vΓ½znam.

## Pod vlastním účtem
### Co gate kontroluje

RepozitΓ‘Ε™ si zkopΓ­ruj, ale **neforkuj ho pro kaΕΎdou organizaci zvlΓ‘Ε‘Ε₯.**
DΔ›dΔ›nΓ­ je levnΔ›jΕ‘Γ­:
`validate.sh` pouΕ‘tΓ­ dvΔ› vΔ›ci a ta druhΓ‘ je dΕ―leΕΎitΔ›jΕ‘Γ­:

- `renovate-config-validator --strict` β€” presety jsou sprΓ‘vnΔ› zapsanΓ©
- `tooling/test_policy.py` β€” presety **rozhodujΓ­** to, co majΓ­

ValidΓ‘tor kontroluje tvar, ne vΓ½znam. KaΕΎdΓ‘ chyba, kterou tenhle preset vydal,
jΓ­m proΕ‘la: neukotvenΓ½ vzor dΓ‘vajΓ­cΓ­ dΕ―vΔ›ru namespace, kterΓ½ nikdo nevlastnΓ­,
pravidlo zaΕ™azenΓ© tak, ΕΎe ruΕ‘ilo to nad sebou, dva neexistujΓ­cΓ­ nΓ‘zvy managerΕ―.
Policy test je mΓ‘ zmrazenΓ© jako pΕ™Γ­pady a spadne, kdyby obnova lockfilu zase
dostala automerge.

Renovate matching reimplementuje, mΓ­sto aby volal Renovate, takΕΎe se od
skutečného enginu může rozejít. SelhÑní je důvod se podívat; průchod je slabőí
dΕ―kaz neΕΎ dry run.

## TΕ™i zpΕ―soby, jak si to vzΓ­t, a co kterΓ½ stojΓ­

**Jen dΔ›dit.** Opravy k tobΔ› dorazΓ­ ve chvΓ­li, kdy vzniknou. Nic neudrΕΎujeΕ‘.

```json
{ "extends": ["github>ownctrl/supply-chain"] }
```

**DΔ›dit a pΕ™ebΓ­t.** TotΓ©ΕΎ plus vlastnΓ­ doplňky. Tohle pouΕΎij pro druhou značku
nebo tΓ½m mΓ­sto forku β€” jedna bezpečnostnΓ­ oprava by se nemΔ›la dΔ›lat tolikrΓ‘t,
kolik mΓ‘Ε‘ kopiΓ­.

```json
{
Expand All @@ -169,7 +193,59 @@ DΔ›dΔ›nΓ­ je levnΔ›jΕ‘Γ­:
}
```

Fork znamenÑ opravit každou bezpečnostní vadu tolikrÑt, kolik mÑő kopií.
**VzΓ­t si kopii.** Tenhle repozitΓ‘Ε™ je GitHub template. SΓ‘hni po nΔ›m, kdyΕΎ
politiku potΕ™ebujeΕ‘ vlastnit β€” odstΕ™iΕΎenΓ© prostΕ™edΓ­, poΕΎadavek na compliance,
nesouhlas s nΔ›jakΓ½m rozhodnutΓ­m tady.

Ale ber to s cenovkou: **kopie pΕ™estane dostΓ‘vat opravy.** V tomhle presetu
se naΕ‘ly čtyΕ™i vady, na kterΓ© by nikdo nepΕ™iΕ‘el čtenΓ­m dokumentace, a kopie
vzatΓ‘ pΕ™ed kteroukoli z nich si ji nechala. U bezpečnostnΓ­ politiky je tohle
ten drahΓ½ smΔ›r.

KdyΕΎ chceΕ‘ vlastnΓ­ adresu **i** opravy, aΕ₯ tvoje kopie dΔ›dΓ­ odsud:

```json
{
"extends": ["github>ownctrl/supply-chain"],
"packageRules": [ /* jen tvoje odchylky */ ]
}
```

Workflow `Setup Owner` po ručnΓ­m spuΕ‘tΔ›nΓ­ pΕ™epΓ­Ε‘e reference a LICENSE na
novΓ©ho vlastnΓ­ka.

## Co to nedΔ›lΓ‘

Preset rozhoduje, kterΓ© aktualizace zΓ‘vislostΓ­ smΔ›jΓ­ pΕ™istΓ‘t bez lidskΓ©ho
pohledu. To je celΓ½ jeho rozsah a stojΓ­ za to Ε™Γ­ct natvrdo, kde končí:

- **HlΓ­dΓ‘ aktualizace, ne to, co uΕΎ mΓ‘Ε‘.** HostilnΓ­ verze, kterΓ‘ ti uΕΎ leΕΎΓ­
v lockfilu, je mimo jeho dosah. Zkontroluj lockfile pΕ™Γ­mo a rotuj vΕ‘echno, na
co ten balíček mohl dosÑhnout.
- **NenΓ­ to skener.** NeprohlΓ­ΕΎΓ­ obsah balíčkΕ―, neovΔ›Ε™uje, ΕΎe vydanΓ½ artefakt
odpovΓ­dΓ‘ zdrojΓ‘ku, ani nedetekuje kompromitaci. Kombinuj ho s něčím, co to
umΓ­.
- **Odstup nenΓ­ zΓ‘ruka.** `minimumReleaseAge` kupuje čas, aby si nΔ›kdo jinΓ½
vΕ‘iml Ε‘patnΓ©ho vydΓ‘nΓ­. NemusΓ­ si vΕ‘imnout nikdo. Shai-Hulud zΕ―stal
u nΔ›kterΓ½ch balíčkΕ― nepovΕ‘imnutΓ½ dΓ©le neΕΎ sedm dnΓ­.
- **Watch list je snímek.** 428 balíčků znÑmých z jednoho útoku v listopadu
2025. O tom pΕ™Γ­Ε‘tΓ­m neΕ™Γ­kΓ‘ nic.
- **Policy test reimplementuje Renovate matching**, mΓ­sto aby volal Renovate,
takže se od skutečného enginu může rozejít.

PoskytovΓ‘no tak, jak je, bez zΓ‘ruky, pod [licencΓ­ MIT](./LICENSE). RozhodnutΓ­,
co smí tvůj projekt sloučit bez dozoru, je tvoje; tenhle preset je výchozí bod
s odΕ―vodnΔ›nΓ­m, ne nΓ‘hrada za to rozhodnutΓ­.

## Workflow `Setup Owner`

`.github/workflows/setup-owner.yml` je tu pro cestu s kopií. Po ručním
spuΕ‘tΔ›nΓ­ pΕ™epΓ­Ε‘e reference presetu a copyright v LICENSE na novΓ©ho vlastnΓ­ka
a pak sΓ‘m sebe smaΕΎe.

BΔ›ΕΎΓ­ s `contents: write` a pushuje na vΓ½chozΓ­ vΔ›tev, coΕΎ k tΓ© prΓ‘ci potΕ™ebuje.
Je jen `workflow_dispatch` β€” nic ho nespouΕ‘tΓ­ automaticky. KdyΕΎ preset dΔ›dΓ­Ε‘
mΓ­sto kopΓ­rovΓ‘nΓ­, nikdy ho nepustΓ­Ε‘.

## VerzovΓ‘nΓ­

Expand Down
89 changes: 82 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div align="center">

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
[![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/)
![Ecosystems](https://img.shields.io/badge/ecosystems-10+-blue.svg)

Expand Down Expand Up @@ -160,11 +160,17 @@ Pin a release if you do not want your policy to change under you:
{ "extends": ["github>ownctrl/supply-chain#v1.0.0"] }
```

### Using it under your own account
### Three ways to adopt this, and what each costs

Copy this repo, then reference your own copy. Do **not** fork it per
organisation β€” inherit instead, so one security fix does not have to be applied
once per copy:
**Just extend it.** Fixes reach you the moment they land. Nothing to maintain.

```json
{ "extends": ["github>ownctrl/supply-chain"] }
```

**Extend it and override.** Same, plus your own additions. Use this for a
second brand or team rather than forking β€” one security fix should not have to
be applied once per copy.

```json
{
Expand All @@ -173,8 +179,26 @@ once per copy:
}
```

The `Setup Owner` workflow rewrites the examples and LICENSE to the new owner
when you dispatch it manually.
**Take a copy.** This repo is a GitHub template. Use it when you need to own
the policy outright β€” an air-gapped mirror, a compliance requirement, a
disagreement with a decision here.

Be clear about the price: **a copy stops receiving fixes.** Four faults were
found in this preset that no amount of reading the documentation would have
surfaced, and a copy taken before each one kept it. For a security policy that
is the expensive direction.

If you want your own address *and* the fixes, have your copy extend upstream:

```json
{
"extends": ["github>ownctrl/supply-chain"],
"packageRules": [ /* your deviations, and only those */ ]
}
```

The `Setup Owner` workflow rewrites the preset references and LICENSE to the
new owner when you dispatch it manually.

### JavaScript runtimes and package managers

Expand Down Expand Up @@ -246,6 +270,23 @@ ln -s ../../tooling/validate.sh .git/hooks/pre-push
A local hook is a convenience, not a boundary β€” anyone can skip it. CI stays the
real gate.

### What the gate checks

`validate.sh` runs two things, and the second matters more:

- `renovate-config-validator --strict` β€” the presets are well formed
- `tooling/test_policy.py` β€” the presets *decide* what they should

The validator checks shape, not meaning. Every fault this preset has shipped
passed it: an unanchored pattern granting trust to a namespace nobody owns, a
rule ordered so it undid the one above it, two manager names that do not exist.
The policy test freezes those as cases, and fails if lockfile maintenance ever
gets automerge back.

It reimplements Renovate's matching rather than calling Renovate, so it can
drift from the real engine. A failure is a reason to look; a pass is weaker
evidence than a dry run.

## Testing locally

You can test this config locally before deploying:
Expand Down Expand Up @@ -309,6 +350,40 @@ The full list lives in [`default.json`](./default.json). Affected version ranges

---

## What this does not do

This preset decides which dependency updates may merge unattended. That is its
whole scope, and it is worth being blunt about the edges:

- **It gates updates, not what you already have.** A hostile version already in
your lockfile is outside its reach. Check the lockfile directly and rotate
anything the package could have read.
- **It is not a scanner.** It does not inspect package contents, verify that a
published artifact matches its source, or detect compromise. Pair it with
something that does.
- **A delay is not a guarantee.** `minimumReleaseAge` buys time for someone
else to notice a bad release. Nobody may notice. Shai-Hulud went unnoticed
for longer than seven days in some packages.
- **The watch list is a snapshot.** 428 packages known compromised in one
attack, in November 2025. It says nothing about the next one.
- **The policy test reimplements Renovate's matching** rather than calling
Renovate, so it can drift from the real engine.

Provided as is, without warranty, under the [MIT licence](./LICENSE). Deciding
what your project may merge unattended is your decision; this preset is a
starting point with reasoning attached, not a substitute for making it.

## The `Setup Owner` workflow

`.github/workflows/setup-owner.yml` exists for the copy path. Dispatch it
manually after taking a copy and it rewrites the preset references and the
LICENSE copyright to whoever now owns the repo, then deletes itself.

It runs with `contents: write` and pushes to the default branch, which is what
it needs to do that job. It is `workflow_dispatch` only β€” nothing triggers it
automatically. If you are extending this preset rather than copying it, you
will never run it.

## Versioning

`main` is what an unpinned `github>ownctrl/supply-chain` resolves to, so every
Expand Down
45 changes: 45 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Security policy

## Reporting

Report a problem with this preset through
[private vulnerability reporting](https://github.com/ownctrl/supply-chain/security/advisories/new),
not a public issue.

Worth reporting:

- a rule that lets something merge unattended when it should not
- a matcher that is broader than it reads β€” an unanchored pattern over a
package prefix nobody owns is the fault this preset has shipped most often
- a package on the watch list that should not be, or one missing that should
- remediation text pointing somewhere a reader cannot follow

## What this preset is and is not

It decides **which dependency updates may land without a human looking at
them.** That is all. It does not scan code, detect compromise, or verify that a
published package matches its source.

It cannot protect a repository that has already installed a hostile version. It
gates *updates*, so a compromised package already in your lockfile is outside
its reach β€” check the lockfile directly and rotate anything the package could
have read.

## Known limitations

- `renovate-config-validator` does not validate manager names. A typo'd manager
silently disables the rule containing it. `tooling/test_policy.py` checks the
names this preset uses against a hardcoded list, which needs updating when
Renovate adds a manager.
- `tooling/test_policy.py` reimplements Renovate's matching rather than calling
Renovate. It can drift from the real engine. A failure is a reason to look; a
pass is weaker evidence than a Renovate dry run.
- `minimumReleaseAge` does not apply to `pin`, `lockFileMaintenance`,
`lockfileUpdate`, `rollback`, `bump` or `replacement`. This preset disables
automerge for lockfile maintenance because of it.
- Renovate has a hosted app on GitHub.com only. On every other forge you run it
yourself, and its token can write to all of your repositories.

## Supported versions

The latest tag. Older tags do not get fixes β€” move the pin.
12 changes: 3 additions & 9 deletions aggressive.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"description": "Low-friction: updates any time, no release-age floor, higher PR limit. Not for use during an active supply-chain incident.",
"extends": [
"local>ownctrl/supply-chain"
],
"schedule": [
"at any time"
],
"extends": ["local>ownctrl/supply-chain"],
"schedule": ["at any time"],
"prConcurrentLimit": 10,
"minimumReleaseAge": null,
"packageRules": [
{
"description": "Top-level minimumReleaseAge cannot lift the inherited npm rule -- a packageRule outranks it, so drop the floor here too",
"matchDatasources": [
"npm"
],
"matchDatasources": ["npm"],
"minimumReleaseAge": null
}
]
Expand Down
19 changes: 17 additions & 2 deletions default.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,14 @@
},
{
"description": "Python: group together",
"matchManagers": ["pip_requirements", "pip-compile", "poetry", "pep621", "pipenv", "setup-cfg"],
"matchManagers": [
"pip_requirements",
"pip-compile",
"poetry",
"pep621",
"pipenv",
"setup-cfg"
],
"groupName": "Python",
"separateMinorPatch": false
},
Expand Down Expand Up @@ -182,7 +189,15 @@
},
{
"description": "Kubernetes: group together",
"matchManagers": ["kubernetes", "helmv3", "helm-values", "helm-requirements", "helmfile", "flux", "argocd"],
"matchManagers": [
"kubernetes",
"helmv3",
"helm-values",
"helm-requirements",
"helmfile",
"flux",
"argocd"
],
"groupName": "Kubernetes",
"separateMinorPatch": false
},
Expand Down
25 changes: 6 additions & 19 deletions lockdown.json
Original file line number Diff line number Diff line change
@@ -1,37 +1,26 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"description": "πŸ”’ Lockdown: no automerge anywhere, 14-day floor, every update waits for dashboard approval. For use during an active supply-chain incident.",
"extends": [
"local>ownctrl/supply-chain"
],
"schedule": [
"before 06:00 on monday"
],
"extends": ["local>ownctrl/supply-chain"],
"schedule": ["before 06:00 on monday"],
"minimumReleaseAge": "14 days",
"prConcurrentLimit": 2,
"dependencyDashboardApproval": true,
"packageRules": [
{
"description": "LOCKDOWN: npm floor to 14 days -- must be a packageRule, the top-level value is outranked by the inherited npm rule",
"matchDatasources": [
"npm"
],
"matchDatasources": ["npm"],
"minimumReleaseAge": "14 days"
},
{
"description": "LOCKDOWN: Disable ALL automerge",
"automerge": false,
"dependencyDashboardApproval": true,
"matchPackageNames": [
"*"
]
"matchPackageNames": ["*"]
},
{
"description": "LOCKDOWN: Production deps require manual approval",
"matchDepTypes": [
"dependencies",
"peerDependencies"
],
"matchDepTypes": ["dependencies", "peerDependencies"],
"prPriority": -50,
"prBodyNotes": [
"πŸ”’ **LOCKDOWN MODE ACTIVE**",
Expand All @@ -47,9 +36,7 @@
},
{
"description": "LOCKDOWN: Block all major updates",
"matchUpdateTypes": [
"major"
],
"matchUpdateTypes": ["major"],
"enabled": false
}
]
Expand Down
8 changes: 2 additions & 6 deletions no-automerge.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"description": "Disable automerge entirely, keeping every other policy from the base preset.",
"extends": [
"local>ownctrl/supply-chain"
],
"extends": ["local>ownctrl/supply-chain"],
"packageRules": [
{
"automerge": false,
"matchPackageNames": [
"*"
]
"matchPackageNames": ["*"]
}
]
}
Loading
Loading