Skip to content

Commit a6c63db

Browse files
authored
feat: yarnrc security (#791)
1 parent 2a4c8db commit a6c63db

5 files changed

Lines changed: 194 additions & 13 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
AI Context: .yarnrc.yml Security (yarnrc-security.mdx)
2+
3+
Source of Information:
4+
1. Webiny v6 example template: packages/create-webiny-project/_templates/base/example.yarnrc.yml
5+
2. Yarn Berry documentation for enableScripts, npmMinimalAgeGate, npmPreapprovedPackages, approvedGitRepositories
6+
7+
Key Documentation Decisions:
8+
1. Framed as a recommendation users opt into — these settings are not yet shipped by default with new projects
9+
2. Positioned as a "get-started" page because users should harden their project early
10+
3. Explained each setting individually with its own section and code examples
11+
4. Deliberately avoided recommending users disable settings globally — always guide toward per-package or scoped exceptions
12+
5. Did not cover non-security .yarnrc.yml settings (nodeLinker, compressionLevel) — those are standard Yarn config, not security-related
13+
6. Did not mention yarnPath or Yarn version — that can change between writing and release
14+
7. Used @webiny/* glob pattern for preapproved packages (matches the template), not individual package names
15+
16+
Understanding of .yarnrc.yml Security Settings:
17+
- enableScripts: false — the primary defense against supply chain attacks via lifecycle scripts (postinstall, preinstall). Most npm malware relies on postinstall hooks.
18+
- npmMinimalAgeGate: 3d — time buffer before newly published packages can be installed. Protects against typosquatting, account takeover, and star-jacking.
19+
- npmPreapprovedPackages — exemptions from the age gate. Uses @webiny/* glob to cover all Webiny packages plus wts-client.
20+
- approvedGitRepositories — restricts git-based dependencies to a whitelist. Only the upgrade repository (webiny-upgrades-v6) is approved.
21+
22+
Related Documents:
23+
- get-started/quickstart.mdx — references prerequisites and project setup where .yarnrc.yml lives
24+
- get-started/upgrade.mdx — the upgrade mechanism uses approvedGitRepositories (webiny-upgrades-v6)
25+
26+
Key Code Locations:
27+
- /webiny-js-v6/packages/create-webiny-project/_templates/base/example.yarnrc.yml — the source template for recommended settings
28+
29+
Tone Guidelines:
30+
- Practical and encouraging — guide users to harden their project, not lecture about security
31+
- Explain the "why" briefly (supply chain attacks) but focus on the "how to set up"
32+
- Never recommend disabling security globally — always guide toward scoped exceptions
33+
- Keep code examples minimal and copy-paste ready
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
---
2+
id: yr5s3c8k
3+
title: .yarnrc.yml Security
4+
description: Harden your Webiny project against supply chain attacks using Yarn security settings.
5+
---
6+
7+
import { Alert } from "@/components/Alert";
8+
9+
<Alert type="success" title="WHAT YOU'LL LEARN">
10+
11+
- why you should harden your Webiny project against supply chain attacks
12+
- which Yarn security settings to add to your `.yarnrc.yml`
13+
- what each setting does and how to customize it
14+
- how to troubleshoot installation failures caused by these settings
15+
16+
</Alert>
17+
18+
## Overview
19+
20+
Every Webiny project uses Yarn as its package manager and has a `.yarnrc.yml` configuration file in the project root. Yarn supports several security settings that you can add to this file to protect your project against supply chain attacks - malicious code that enters your application through compromised or deceptive npm packages.
21+
22+
This page explains the settings Webiny recommends and how to add them to your project.
23+
24+
## Recommended Settings
25+
26+
Add the following settings to the `.yarnrc.yml` file in your project root:
27+
28+
```yaml .yarnrc.yml
29+
enableScripts: false
30+
31+
npmMinimalAgeGate: 3d
32+
npmPreapprovedPackages:
33+
- "@webiny/*"
34+
- "wts-client"
35+
36+
approvedGitRepositories:
37+
- "https://github.com/webiny/webiny-upgrades-v6"
38+
```
39+
40+
The sections below explain what each setting does and how to adjust it for your needs.
41+
42+
## Lifecycle Scripts
43+
44+
```yaml .yarnrc.yml
45+
enableScripts: false
46+
```
47+
48+
When `enableScripts` is set to `false`, Yarn skips all package lifecycle scripts - `postinstall`, `preinstall`, `install`, and similar hooks defined in a dependency's `package.json`. This is the single most effective defense against malicious packages, since the majority of supply chain attacks rely on lifecycle scripts to execute arbitrary code during installation.
49+
50+
<Alert type="warning" title="When a dependency requires scripts">
51+
52+
Some legitimate packages (native modules, packages that compile binaries) rely on `postinstall` scripts. If you add such a dependency and it does not work after installation, you may need to explicitly allow scripts for that package. See [Allowing Scripts for Specific Packages](#allowing-scripts-for-specific-packages) below.
53+
54+
</Alert>
55+
56+
### Allowing Scripts for Specific Packages
57+
58+
Rather than setting `enableScripts` back to `true` globally, allow scripts only for the packages that need them by adding a `packageExtensions` entry:
59+
60+
```yaml .yarnrc.yml
61+
enableScripts: false
62+
63+
packageExtensions:
64+
"some-native-package@*":
65+
scripts:
66+
postinstall: "node ./build.js"
67+
```
68+
69+
## Package Age Gate
70+
71+
```yaml .yarnrc.yml
72+
npmMinimalAgeGate: 3d
73+
```
74+
75+
The `npmMinimalAgeGate` setting tells Yarn to reject any package version that was published to the npm registry less than the specified duration ago. This creates a time buffer that helps protect against:
76+
77+
- **Typosquatting** - malicious packages with names similar to popular ones
78+
- **Account takeover** - compromised maintainer accounts pushing malicious updates
79+
- **Star-jacking** - newly published malicious packages designed to look trustworthy
80+
81+
With a value of `3d`, you cannot install a package version that was published less than three days ago. This gives the community and automated scanners time to flag malicious releases before they reach your project.
82+
83+
### Preapproved Packages
84+
85+
```yaml .yarnrc.yml
86+
npmPreapprovedPackages:
87+
- "@webiny/*"
88+
- "wts-client"
89+
```
90+
91+
Packages listed under `npmPreapprovedPackages` are exempt from the age gate. The `@webiny/*` glob pattern covers all Webiny packages, which are published by the Webiny team and need to be installable immediately after release.
92+
93+
If you publish your own packages or work with a trusted vendor whose packages you need immediately after release, add them to the list:
94+
95+
```yaml .yarnrc.yml
96+
npmPreapprovedPackages:
97+
- "@webiny/*"
98+
- "wts-client"
99+
- "@your-org/*"
100+
```
101+
102+
### Adjusting the Age Gate Duration
103+
104+
You can increase or decrease the duration to match your risk tolerance:
105+
106+
```yaml .yarnrc.yml
107+
# More conservative - wait a full week
108+
npmMinimalAgeGate: 7d
109+
110+
# Less conservative - wait one day
111+
npmMinimalAgeGate: 1d
112+
```
113+
114+
A longer duration provides more protection but delays access to new releases. A shorter duration gives faster access but reduces the window for malicious packages to be caught.
115+
116+
## Approved Git Repositories
117+
118+
```yaml .yarnrc.yml
119+
approvedGitRepositories:
120+
- "https://github.com/webiny/webiny-upgrades-v6"
121+
```
122+
123+
The `approvedGitRepositories` setting restricts which Git repositories Yarn is allowed to use as a dependency source. Any `git+https://...` or `github:...` dependency that does not match an entry in this list is rejected.
124+
125+
Webiny uses this to allow the upgrade repository - the mechanism for applying Webiny version upgrades - while blocking all other Git-based dependencies by default.
126+
127+
### Adding a Git Repository
128+
129+
If your project needs a dependency from a Git repository, add its URL to the list:
130+
131+
```yaml .yarnrc.yml
132+
approvedGitRepositories:
133+
- "https://github.com/webiny/webiny-upgrades-v6"
134+
- "https://github.com/your-org/your-private-package"
135+
```
136+
137+
## Troubleshooting
138+
139+
### "Package was published less than X ago"
140+
141+
The age gate blocked a package version. You have three options:
142+
143+
- **Wait** - try again after the age gate duration has passed
144+
- **Preapprove** - add the package to `npmPreapprovedPackages` if you trust the publisher
145+
- **Lower the gate** - reduce `npmMinimalAgeGate` (not recommended unless you understand the risk)
146+
147+
### "Lifecycle scripts are disabled"
148+
149+
A package tried to run a script during installation but `enableScripts: false` blocked it. If the package needs scripts to function correctly, allow them for that specific package rather than enabling scripts globally.
150+
151+
### "Git repository is not approved"
152+
153+
A dependency points to a Git repository that is not in `approvedGitRepositories`. Add the repository URL to the list if you trust it.

docs/developer-docs/6.x/navigation.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export const Navigation = ({ children }: { children: React.ReactNode }) => {
181181
<Page link={"infrastructure/github-actions"} title={"GitHub Actions"} />
182182
<Page link={"infrastructure/dynamodb-only"} title={"DynamoDB-Only Dev Environments"} />
183183
<Page link={"infrastructure/opensearch"} title={"Shared OpenSearch Cluster"} />
184+
<Page link={"infrastructure/yarnrc-security"} />
184185
<Group title={"Extensions"} link={"infrastructure/extensions/aws-tags"}>
185186
<Page link={"infrastructure/extensions/aws-tags"} title={"AWS Tags"} />
186187
<Page

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,9 @@
124124
"tsx": "^4.22.3",
125125
"typescript": "^5.9.3"
126126
},
127-
"packageManager": "yarn@4.15.0"
127+
"packageManager": "yarn@4.15.0",
128+
"resolutions": {
129+
"@types/react": "18.3.29",
130+
"@types/react-dom": "18.3.7"
131+
}
128132
}

yarn.lock

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3686,7 +3686,7 @@ __metadata:
36863686
languageName: node
36873687
linkType: hard
36883688

3689-
"@types/react-dom@npm:^18.3.7":
3689+
"@types/react-dom@npm:18.3.7":
36903690
version: 18.3.7
36913691
resolution: "@types/react-dom@npm:18.3.7"
36923692
peerDependencies:
@@ -3704,17 +3704,7 @@ __metadata:
37043704
languageName: node
37053705
linkType: hard
37063706

3707-
"@types/react@npm:18.3.28":
3708-
version: 18.3.28
3709-
resolution: "@types/react@npm:18.3.28"
3710-
dependencies:
3711-
"@types/prop-types": "npm:*"
3712-
csstype: "npm:^3.2.2"
3713-
checksum: 10/6db7bb7f19957ee9f530baa7d143527f8befedad1585b064eb80777be0d84621157de75aba4f499ff429b10c5ef0c7d13e89be6bca3296ef71c80472894ff0eb
3714-
languageName: node
3715-
linkType: hard
3716-
3717-
"@types/react@npm:^18, @types/react@npm:^18.3.29":
3707+
"@types/react@npm:18.3.29":
37183708
version: 18.3.29
37193709
resolution: "@types/react@npm:18.3.29"
37203710
dependencies:

0 commit comments

Comments
 (0)