chore(security.txt): make compliant by adding an Expires field#9005
chore(security.txt): make compliant by adding an Expires field#9005kobihikri wants to merge 3 commits into
Expires field#9005Conversation
RFC 9116 section 2.5.5 states the Expires field MUST be present in a security.txt file. The published file at nodejs.org/.well-known/security.txt currently has only Contact and Policy, so a scanner or researcher following the RFC has no signed expiry to validate the file against. This adds an Expires value roughly a year out. Renewing it (or automating the refresh) keeps the file trustworthy per the RFC; happy to wire that up if you prefer, but kept this change to the single missing field. Signed-off-by: kobihikri <kobi.hikri@gmail.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryLow Risk Overview Adds Reviewed by Cursor Bugbot for commit e2a51ca. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
@nodejs/security-wg should own this file and i's changes |
bjohansebas
left a comment
There was a problem hiding this comment.
Hmm, I think we should have some automation to update that automatically. I feel like that field is going to be forgotten, and we don't want to show that it's expired when all the other information is actually up to date.
cc: @nodejs/web-infra
|
👍 Agreed, a once-per-year cron to open a PR to bump it would be nice. That'll prompt folks to actually review the information is still correct before the date is bumped. |
|
@kobihikri Something like that could help you build it https://github.com/expressjs/expressjs.com/blob/main/.github/workflows/update-external-docs.yml#L13-L57 |
|
I can take on this PR, but I’m not familiar with your internal rules. Can I take the PR?
Get Outlook for iOS<https://aka.ms/o0ukef>
…________________________________
From: Matt Cowley ***@***.***>
Sent: Tuesday, 14 July 2026 19:06:23
To: nodejs/nodejs.org ***@***.***>
Cc: Kobi Hikri ***@***.***>; Author ***@***.***>
Subject: Re: [nodejs/nodejs.org] security.txt: add required Expires field (PR #9005)
[https://avatars.githubusercontent.com/u/12371363?s=20&v=4]MattIPv4 left a comment (nodejs/nodejs.org#9005)<#9005 (comment)>
👍 Agreed, a once-per-year cron to open a PR to bump it would be nice. That'll prompt folks to actually review the information is still correct before the date is bumped.
—
Reply to this email directly, view it on GitHub<#9005?email_source=notifications&email_token=ABB2WAJ2BKCTD6KFKSX52WD5EZK77A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOJXGEZTGMBZG432M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-4971330977>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/ABB2WAM5TC2DCFLGASWME6T5EZK77AVCNFSNUABEKJSXA33TNF2G64TZHMZTONZRG42DONB3JFZXG5LFHM2DQOBRGI3TIMJYGWQXMAQ>.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
|
I’m on it.
…On Tue, 14 Jul 2026 at 19:15 Kobi Hikri ***@***.***> wrote:
*kobihikri* left a comment (nodejs/nodejs.org#9005)
<#9005 (comment)>
I can take on this PR, but I’m not familiar with your internal rules. Can
I take the PR?
Get Outlook for iOS<https://aka.ms/o0ukef>
________________________________
From: Matt Cowley ***@***.***>
Sent: Tuesday, 14 July 2026 19:06:23
To: nodejs/nodejs.org ***@***.***>
Cc: Kobi Hikri ***@***.***>; Author ***@***.***>
Subject: Re: [nodejs/nodejs.org] security.txt: add required Expires field
(PR #9005)
[https://avatars.githubusercontent.com/u/12371363?s=20&v=4]MattIPv4 left
a comment (nodejs/nodejs.org#9005)<
#9005 (comment)>
👍 Agreed, a once-per-year cron to open a PR to bump it would be nice.
That'll prompt folks to actually review the information is still correct
before the date is bumped.
—
Reply to this email directly, view it on GitHub<
#9005?email_source=notifications&email_token=ABB2WAJ2BKCTD6KFKSX52WD5EZK77A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOJXGEZTGMBZG432M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-4971330977>,
or unsubscribe<
https://github.com/notifications/unsubscribe-auth/ABB2WAM5TC2DCFLGASWME6T5EZK77AVCNFSNUABEKJSXA33TNF2G64TZHMZTONZRG42DONB3JFZXG5LFHM2DQOBRGI3TIMJYGWQXMAQ>.
You are receiving this because you authored the thread.Message ID:
***@***.***>
—
Reply to this email directly, view it on GitHub
<#9005?email_source=notifications&email_token=ABB2WAKACI3J7R6NFCVETND5EZMDZA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOJXGE2DCOJZHA22M4TFMFZW63VNPFXXK4S7MFRXI2LWNF2HTJLFOZSW45FMMZXW65DFOJPWG3DJMNVQ#issuecomment-4971419985>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABB2WAK3MR6LSQAOJS2SLO35EZMDZAVCNFSNUABEKJSXA33TNF2G64TZHMZTONZRG42DONB3JFZXG5LFHM2DQOBRGI3TIMJYGWQXMAQ>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
Addresses review feedback that a static Expires field will be forgotten and silently expire. Monthly job: if Expires is within 30 days, bump it a year and open a PR for a human to confirm the contact/policy are still valid. Modeled on expressjs/expressjs.com's update-external-docs workflow (as suggested in review) and uses the same gr2m/create-or-update-pull-request-action pin this repo already uses. Assisted by an AI tool; verified myself. Signed-off-by: Kobi Hikri <kobi.hikri@gmail.com>
|
Good call, and thanks for the pointer @bjohansebas — you're right that a static I've pushed a workflow modeled on the Happy to adjust the cadence, the labels, or hand the workflow off to |
| run: | | ||
| set -euo pipefail | ||
| new=$(date -u -d '+365 days' +%Y-%m-%dT23:59:59.000Z) | ||
| sed -i "s/^Expires:.*/Expires: ${new}/I" "$SECURITY_TXT" |
There was a problem hiding this comment.
Expires bump drifts each cycle
Low Severity
The bump sets Expires to today plus 365 days whenever the current value is within 30 days, rather than advancing the existing Expires value by a year. With the monthly-on-the-1st cron, that early window makes the anniversary shift about a month earlier each cycle, so the automated PR does not actually extend the prior expiry by one year.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 0679b62. Configure here.
Per review: switch from a monthly 'within 30 days -> today+365' check (which drifts the window earlier each cycle) to a once-a-year cron that always sets Expires ~13 months out. The >1yr window means the file never expires between runs, and the yearly PR prompts a human to re-confirm the contact/policy. Signed-off-by: Kobi Hikri <kobi.hikri@gmail.com>
|
Thanks @MattIPv4 — that's exactly the intent. And good catch on the drift (via the bot review): I've reworked it into a once-a-year cron that always sets It still opens a PR rather than committing directly, so a human re-confirms the |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e2a51ca. Configure here.
| SECURITY_TXT: apps/site/public/.well-known/security.txt | ||
| run: | | ||
| set -euo pipefail | ||
| new=$(date -u -d '+400 days' +%Y-%m-%dT23:59:59.000Z) |
There was a problem hiding this comment.
Yearly refresh leaves thin expiry buffer
Medium Severity
The yearly cron plus a human-merge PR only leaves about 35 days of headroom in steady state (+400 days minus one year). A slow review over the Jan 1 holiday window, or a single missed scheduled run, lets Expires lapse — the opposite of the workflow’s “never silently lapses” goal. The header’s claim that a >1yr bump protects against delay only covers cron slippage, not merge delay or a skipped annual run.
Reviewed by Cursor Bugbot for commit e2a51ca. Configure here.
| SECURITY_TXT: apps/site/public/.well-known/security.txt | ||
| run: | | ||
| set -euo pipefail | ||
| new=$(date -u -d '+400 days' +%Y-%m-%dT23:59:59.000Z) |
There was a problem hiding this comment.
Expires bump exceeds RFC one-year limit
Low Severity
The refresh sets Expires with +400 days (~13 months). RFC 9116 §2.5.5 recommends the value be less than a year into the future. This PR’s goal is RFC conformance for scanners that flag a missing Expires; the automated bump will itself produce values those same tools often warn on.
Reviewed by Cursor Bugbot for commit e2a51ca. Configure here.
| pull-requests: write | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
There was a problem hiding this comment.
Run pinact -u on this file, please
| # Refreshes apps/site/public/.well-known/security.txt so its RFC 9116 Expires | ||
| # field never silently lapses. Runs once a year and opens a PR bumping Expires | ||
| # ~13 months out — a human must confirm the contact/policy are still valid | ||
| # before merging. The generous window (>1yr) means the file never expires even | ||
| # if a run is delayed, and anchoring to the fixed yearly run avoids drift. |
There was a problem hiding this comment.
| # Refreshes apps/site/public/.well-known/security.txt so its RFC 9116 Expires | |
| # field never silently lapses. Runs once a year and opens a PR bumping Expires | |
| # ~13 months out — a human must confirm the contact/policy are still valid | |
| # before merging. The generous window (>1yr) means the file never expires even | |
| # if a run is delayed, and anchoring to the fixed yearly run avoids drift. | |
| # Security Notes | |
| # Only selected Actions are allowed within this repository. Please refer to (https://github.com/nodejs/nodejs.org/settings/actions) | |
| # for the full list of available actions. If you want to add a new one, please reach out a maintainer with Admin permissions. | |
| # REVIEWERS, please always double-check security practices before merging a PR that contains Workflow changes!! | |
| # AUTHORS, please only use actions with explicit SHA references, and avoid using `@master` or `@main` references or `@version` tags. |
There was a problem hiding this comment.
Let's call this bump-security-txt-expiry.yml so it's clear this bumps it
| - name: Bump Expires (~13 months out) | ||
| env: | ||
| SECURITY_TXT: apps/site/public/.well-known/security.txt | ||
| run: | | ||
| set -euo pipefail | ||
| new=$(date -u -d '+400 days' +%Y-%m-%dT23:59:59.000Z) | ||
| sed -i "s/^Expires:.*/Expires: ${new}/I" "$SECURITY_TXT" |
There was a problem hiding this comment.
As Cursor said, we should bump the in-place year, not re-calculate a date, e.g. Expiry: \d{4} gets replaced by (v) => v + 1
| new=$(date -u -d '+400 days' +%Y-%m-%dT23:59:59.000Z) | ||
| sed -i "s/^Expires:.*/Expires: ${new}/I" "$SECURITY_TXT" | ||
|
|
||
| - name: Create Pull Request (only if the date changed) |
There was a problem hiding this comment.
| - name: Create Pull Request (only if the date changed) | |
| - name: Open pull request |
Conformity with our other workflows. No need to add the (...), this will always bump the date
| commit-message: 'chore: refresh security.txt Expires' | ||
| title: 'chore: refresh security.txt Expires' | ||
| body: | | ||
| Annual refresh of the `Expires` field in `apps/site/public/.well-known/security.txt` so it | ||
| never silently lapses. | ||
|
|
||
| **Before merging, please confirm the rest of the file is still accurate:** | ||
| - The `Contact` (HackerOne) is still the correct, actively-monitored channel | ||
| - The linked `Policy` is still the intended vulnerability-disclosure path | ||
|
|
||
| If anything has changed, update it in this PR before extending the expiration. | ||
|
|
||
| cc: @nodejs/security-wg | ||
| labels: security | ||
| branch: automated/security-txt-expiry |
There was a problem hiding this comment.
| commit-message: 'chore: refresh security.txt Expires' | |
| title: 'chore: refresh security.txt Expires' | |
| body: | | |
| Annual refresh of the `Expires` field in `apps/site/public/.well-known/security.txt` so it | |
| never silently lapses. | |
| **Before merging, please confirm the rest of the file is still accurate:** | |
| - The `Contact` (HackerOne) is still the correct, actively-monitored channel | |
| - The linked `Policy` is still the intended vulnerability-disclosure path | |
| If anything has changed, update it in this PR before extending the expiration. | |
| cc: @nodejs/security-wg | |
| labels: security | |
| branch: automated/security-txt-expiry | |
| update-pull-request-title-and-body: true | |
| branch: chore/update-security-txt | |
| body: | | |
| Updates the `Expiry` on our [`security.txt`](https://nodejs.org/.well-known/security.txt] from ${{ steps.update-expiry.outputs.old }} to ${{ steps.update-expiry.outputs.new }} | |
| Before merging, please explicitly confirm that: | |
| - The security contact listed is still correct and actively monitored | |
| - The linked security policy is still accurate | |
| - The project's preferred vulnerability disclosure path has not changed | |
| cc @nodejs/security-wg | |
| Check this workflow's logs at ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}. | |
| commit-message: 'meta: update security `Expiry` from ${{ steps.update-expiry.outputs.old }} to ${{ steps.update-expiry.outputs.new }}' | |
| title: 'meta: update security `Expiry` from ${{ steps.update-expiry.outputs.old }} to ${{ steps.update-expiry.outputs.new }}' | |
| draft: true |
For conformity
Expires field


Description
The published security.txt at
https://nodejs.org/.well-known/security.txtcurrently contains only:RFC 9116 §2.5.5 states the
Expiresfield MUST be present. Without it, a scanner or security researcher following the RFC has no signed expiry to validate the file against (some tooling flags asecurity.txtwith noExpiresas non-conformant).This PR adds the single missing field:
Contact: https://hackerone.com/nodejs +Expires: 2027-06-30T23:59:59.000Z Policy: https://github.com/nodejs/node/security/policy#securityI set the value roughly a year out. Since a static date will itself lapse eventually, you may want to automate the refresh — glad to wire that up in a follow-up, but I kept this change scoped to the one missing mandatory field.
Check List
Signed-off-by).I used AI assistance to help identify and draft this; I verified the RFC reference and the live file myself and take responsibility for the change. Thank you for maintaining nodejs.org.