Skip to content

Commit 9daa9da

Browse files
committed
Merge branch 'master' into add-tests
2 parents 201cba5 + df65455 commit 9daa9da

File tree

148 files changed

+9724
-1382
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+9724
-1382
lines changed

.github/SECURITY.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
# Security Policy
22

3-
The Caddy project would like to make sure that it stays on top of all practically-exploitable vulnerabilities.
3+
The Caddy project would like to make sure that it stays on top of all relevant and practically-exploitable vulnerabilities.
44

55

66
## Supported Versions
77

8-
| Version | Supported |
9-
| -------- | ----------|
10-
| 2.latest | ✔️ |
11-
| 1.x | :x: |
12-
| < 1.x | :x: |
8+
| Version | Supported |
9+
| ----------- | ----------|
10+
| 2.latest | ✔️ |
11+
| <= 2.latest | :x: |
1312

1413

1514
## Acceptable Scope
@@ -18,21 +17,25 @@ A security report must demonstrate a security bug in the source code from this r
1817

1918
Some security problems are the result of interplay between different components of the Web, rather than a vulnerability in the web server itself. Please only report vulnerabilities in the web server itself, as we cannot coerce the rest of the Web to be fixed (for example, we do not consider IP spoofing, BGP hijacks, or missing/misconfigured HTTP headers a vulnerability in the Caddy web server).
2019

21-
Vulnerabilities caused by misconfigurations are out of scope. Yes, it is entirely possible to craft and use a configuration that is unsafe, just like with every other web server; we recommend against doing that.
20+
Vulnerabilities caused by misconfigurations are out of scope. Yes, it is entirely possible to craft and use a configuration that is unsafe, just like with every other web server; we recommend against doing that. Similarly, external misconfigurations are out of scope. For example, an open or forwarded port from a public network to a Caddy instance intended to serve only internal clients is not a vulnerability in Caddy.
2221

2322
We do not accept reports if the steps imply or require a compromised system or third-party software, as we cannot control those. We expect that users secure their own systems and keep all their software patched. For example, if untrusted users are able to upload/write/host arbitrary files in the web root directory, it is NOT a security bug in Caddy if those files get served to clients; however, it _would_ be a valid report if a bug in Caddy's source code unintentionally gave unauthorized users the ability to upload unsafe files or delete files without relying on an unpatched system or piece of software.
2423

2524
Client-side exploits are out of scope. In other words, it is not a bug in Caddy if the web browser does something unsafe, even if the downloaded content was served by Caddy. (Those kinds of exploits can generally be mitigated by proper configuration of HTTP headers.) As a general rule, the content served by Caddy is not considered in scope because content is configurable by the site owner or the associated web application.
2625

2726
Security bugs in code dependencies (including Go's standard library) are out of scope. Instead, if a dependency has patched a relevant security bug, please feel free to open a public issue or pull request to update that dependency in our code.
2827

28+
We accept security reports and patches, but do not assign CVEs, for code that has not been released with a non-prerelease tag.
29+
2930

3031
## Reporting a Vulnerability
3132

3233
We get a lot of difficult reports that turn out to be invalid. Clear, obvious reports tend to be the most credible (but are also rare).
3334

3435
First please ensure your report falls within the accepted scope of security bugs (above).
3536

37+
:warning: **YOU MUST DISCLOSE WHETHER YOU USED LLMs ("AI") IN ANY WAY.** Whether you are using AI for discovery, as part of writing the report or its replies, and/or testing or validating proofs and changes, we require you to mention the extent of it. **FAILURE TO INCLUDE A DISCLOSURE EVEN IF YOU DO NOT USE AI MAY LEAD TO IMMEDIATE DISMISSAL OF YOUR REPORT AND POTENTIAL BLOCKLISTING.** We will not waste our time chatting with bots. But if you're a human, pull up a chair and we'll drink some chocolate milk.
38+
3639
We'll need enough information to verify the bug and make a patch. To speed things up, please include:
3740

3841
- Most minimal possible config (without redactions!)

.github/workflows/ai.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ jobs:
1616
models: read
1717
contents: read
1818
steps:
19-
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
20-
- uses: github/ai-moderator@6bcdb2a79c2e564db8d76d7d4439d91a044c4eb6
19+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
20+
- uses: github/ai-moderator@81159c370785e295c97461ade67d7c33576e9319
2121
with:
2222
token: ${{ secrets.GITHUB_TOKEN }}
2323
spam-label: 'spam'
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
name: Release Proposal Approval Tracker
2+
3+
on:
4+
pull_request_review:
5+
types: [submitted, dismissed]
6+
pull_request:
7+
types: [labeled, unlabeled, synchronize, closed]
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
issues: write
13+
14+
jobs:
15+
check-approvals:
16+
name: Track Maintainer Approvals
17+
runs-on: ubuntu-latest
18+
# Only run on PRs with release-proposal label
19+
if: contains(github.event.pull_request.labels.*.name, 'release-proposal') && github.event.pull_request.state == 'open'
20+
21+
steps:
22+
- name: Check approvals and update PR
23+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
24+
env:
25+
MAINTAINER_LOGINS: ${{ secrets.MAINTAINER_LOGINS }}
26+
with:
27+
script: |
28+
const pr = context.payload.pull_request;
29+
30+
// Extract version from PR title (e.g., "Release Proposal: v1.2.3")
31+
const versionMatch = pr.title.match(/Release Proposal:\s*(v[\d.]+(?:-[\w.]+)?)/);
32+
const commitMatch = pr.body.match(/\*\*Target Commit:\*\*\s*`([a-f0-9]+)`/);
33+
34+
if (!versionMatch || !commitMatch) {
35+
console.log('Could not extract version from title or commit from body');
36+
return;
37+
}
38+
39+
const version = versionMatch[1];
40+
const targetCommit = commitMatch[1];
41+
42+
console.log(`Version: ${version}, Target Commit: ${targetCommit}`);
43+
44+
// Get all reviews
45+
const reviews = await github.rest.pulls.listReviews({
46+
owner: context.repo.owner,
47+
repo: context.repo.repo,
48+
pull_number: pr.number
49+
});
50+
51+
// Get list of maintainers
52+
const maintainerLoginsRaw = process.env.MAINTAINER_LOGINS || '';
53+
const maintainerLogins = maintainerLoginsRaw
54+
.split(/[,;]/)
55+
.map(login => login.trim())
56+
.filter(login => login.length > 0);
57+
58+
console.log(`Maintainer logins: ${maintainerLogins.join(', ')}`);
59+
60+
// Get the latest review from each user
61+
const latestReviewsByUser = {};
62+
reviews.data.forEach(review => {
63+
const username = review.user.login;
64+
if (!latestReviewsByUser[username] || new Date(review.submitted_at) > new Date(latestReviewsByUser[username].submitted_at)) {
65+
latestReviewsByUser[username] = review;
66+
}
67+
});
68+
69+
// Count approvals from maintainers
70+
const maintainerApprovals = Object.entries(latestReviewsByUser)
71+
.filter(([username, review]) =>
72+
maintainerLogins.includes(username) &&
73+
review.state === 'APPROVED'
74+
)
75+
.map(([username, review]) => username);
76+
77+
const approvalCount = maintainerApprovals.length;
78+
console.log(`Found ${approvalCount} maintainer approvals from: ${maintainerApprovals.join(', ')}`);
79+
80+
// Get current labels
81+
const currentLabels = pr.labels.map(label => label.name);
82+
const hasApprovedLabel = currentLabels.includes('approved');
83+
const hasAwaitingApprovalLabel = currentLabels.includes('awaiting-approval');
84+
85+
if (approvalCount >= 2 && !hasApprovedLabel) {
86+
console.log('✅ Quorum reached! Updating PR...');
87+
88+
// Remove awaiting-approval label if present
89+
if (hasAwaitingApprovalLabel) {
90+
await github.rest.issues.removeLabel({
91+
owner: context.repo.owner,
92+
repo: context.repo.repo,
93+
issue_number: pr.number,
94+
name: 'awaiting-approval'
95+
}).catch(e => console.log('Label not found:', e.message));
96+
}
97+
98+
// Add approved label
99+
await github.rest.issues.addLabels({
100+
owner: context.repo.owner,
101+
repo: context.repo.repo,
102+
issue_number: pr.number,
103+
labels: ['approved']
104+
});
105+
106+
// Add comment with tagging instructions
107+
const approversList = maintainerApprovals.map(u => `@${u}`).join(', ');
108+
const commentBody = [
109+
'## ✅ Approval Quorum Reached',
110+
'',
111+
`This release proposal has been approved by ${approvalCount} maintainers: ${approversList}`,
112+
'',
113+
'### Tagging Instructions',
114+
'',
115+
'A maintainer should now create and push the signed tag:',
116+
'',
117+
'```bash',
118+
`git checkout ${targetCommit}`,
119+
`git tag -s ${version} -m "Release ${version}"`,
120+
`git push origin ${version}`,
121+
`git checkout -`,
122+
'```',
123+
'',
124+
'The release workflow will automatically start when the tag is pushed.'
125+
].join('\n');
126+
127+
await github.rest.issues.createComment({
128+
owner: context.repo.owner,
129+
repo: context.repo.repo,
130+
issue_number: pr.number,
131+
body: commentBody
132+
});
133+
134+
console.log('Posted tagging instructions');
135+
} else if (approvalCount < 2 && hasApprovedLabel) {
136+
console.log('⚠️ Approval count dropped below quorum, removing approved label');
137+
138+
// Remove approved label
139+
await github.rest.issues.removeLabel({
140+
owner: context.repo.owner,
141+
repo: context.repo.repo,
142+
issue_number: pr.number,
143+
name: 'approved'
144+
}).catch(e => console.log('Label not found:', e.message));
145+
146+
// Add awaiting-approval label
147+
if (!hasAwaitingApprovalLabel) {
148+
await github.rest.issues.addLabels({
149+
owner: context.repo.owner,
150+
repo: context.repo.repo,
151+
issue_number: pr.number,
152+
labels: ['awaiting-approval']
153+
});
154+
}
155+
} else {
156+
console.log(`⏳ Waiting for more approvals (${approvalCount}/2 required)`);
157+
}
158+
159+
handle-pr-closed:
160+
name: Handle PR Closed Without Tag
161+
runs-on: ubuntu-latest
162+
if: |
163+
contains(github.event.pull_request.labels.*.name, 'release-proposal') &&
164+
github.event.action == 'closed' && !contains(github.event.pull_request.labels.*.name, 'released')
165+
166+
steps:
167+
- name: Add cancelled label and comment
168+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
169+
with:
170+
script: |
171+
const pr = context.payload.pull_request;
172+
173+
// Check if the release-in-progress label is present
174+
const hasReleaseInProgress = pr.labels.some(label => label.name === 'release-in-progress');
175+
176+
if (hasReleaseInProgress) {
177+
// PR was closed while release was in progress - this is unusual
178+
await github.rest.issues.createComment({
179+
owner: context.repo.owner,
180+
repo: context.repo.repo,
181+
issue_number: pr.number,
182+
body: '⚠️ **Warning:** This PR was closed while a release was in progress. This may indicate an error. Please verify the release status.'
183+
});
184+
} else {
185+
// PR was closed before tag was created - this is normal cancellation
186+
const versionMatch = pr.title.match(/Release Proposal:\s*(v[\d.]+(?:-[\w.]+)?)/);
187+
const version = versionMatch ? versionMatch[1] : 'unknown';
188+
189+
await github.rest.issues.createComment({
190+
owner: context.repo.owner,
191+
repo: context.repo.repo,
192+
issue_number: pr.number,
193+
body: `## 🚫 Release Proposal Cancelled\n\nThis release proposal for ${version} was closed without creating the tag.\n\nIf you want to proceed with this release later, you can create a new release proposal.`
194+
});
195+
}
196+
197+
// Add cancelled label
198+
await github.rest.issues.addLabels({
199+
owner: context.repo.owner,
200+
repo: context.repo.repo,
201+
issue_number: pr.number,
202+
labels: ['cancelled']
203+
});
204+
205+
// Remove other workflow labels if present
206+
const labelsToRemove = ['awaiting-approval', 'approved', 'release-in-progress'];
207+
for (const label of labelsToRemove) {
208+
try {
209+
await github.rest.issues.removeLabel({
210+
owner: context.repo.owner,
211+
repo: context.repo.repo,
212+
issue_number: pr.number,
213+
name: label
214+
});
215+
} catch (e) {
216+
console.log(`Label ${label} not found or already removed`);
217+
}
218+
}
219+
220+
console.log('Added cancelled label and cleaned up workflow labels');
221+

.github/workflows/ci.yml

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ jobs:
3131
- mac
3232
- windows
3333
go:
34-
- '1.25'
34+
- '1.26'
3535

3636
include:
3737
# Set the minimum Go patch version for the given Go minor
3838
# Usable via ${{ matrix.GO_SEMVER }}
39-
- go: '1.25'
40-
GO_SEMVER: '~1.25.0'
39+
- go: '1.26'
40+
GO_SEMVER: '~1.26.0'
4141

4242
# Set some variables per OS, usable via ${{ matrix.VAR }}
4343
# OS_LABEL: the VM label from GitHub Actions (see https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories)
@@ -65,15 +65,15 @@ jobs:
6565
actions: write # to allow uploading artifacts and cache
6666
steps:
6767
- name: Harden the runner (Audit all outbound calls)
68-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
68+
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
6969
with:
7070
egress-policy: audit
7171

7272
- name: Checkout code
73-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
73+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
7474

7575
- name: Install Go
76-
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
76+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
7777
with:
7878
go-version: ${{ matrix.GO_SEMVER }}
7979
check-latest: true
@@ -120,7 +120,7 @@ jobs:
120120
./caddy stop
121121
122122
- name: Publish Build Artifact
123-
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
123+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
124124
with:
125125
name: caddy_${{ runner.os }}_go${{ matrix.go }}_${{ steps.vars.outputs.short_sha }}
126126
path: ${{ matrix.CADDY_BIN_PATH }}
@@ -162,13 +162,13 @@ jobs:
162162
continue-on-error: true # August 2020: s390x VM is down due to weather and power issues
163163
steps:
164164
- name: Harden the runner (Audit all outbound calls)
165-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
165+
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
166166
with:
167167
egress-policy: audit
168168
allowed-endpoints: ci-s390x.caddyserver.com:22
169169

170170
- name: Checkout code
171-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
171+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
172172
- name: Run Tests
173173
run: |
174174
set +e
@@ -221,27 +221,27 @@ jobs:
221221
if: github.event.pull_request.head.repo.full_name == 'caddyserver/caddy' && github.actor != 'dependabot[bot]'
222222
steps:
223223
- name: Harden the runner (Audit all outbound calls)
224-
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
224+
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
225225
with:
226226
egress-policy: audit
227227

228228
- name: Checkout code
229-
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
229+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
230230

231-
- uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
231+
- uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
232232
with:
233233
version: latest
234234
args: check
235235
- name: Install Go
236-
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
236+
uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
237237
with:
238-
go-version: "~1.25"
238+
go-version: "~1.26"
239239
check-latest: true
240240
- name: Install xcaddy
241241
run: |
242242
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
243243
xcaddy version
244-
- uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
244+
- uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0
245245
with:
246246
version: latest
247247
args: build --single-target --snapshot

0 commit comments

Comments
 (0)