Skip to content

Add RFC 9116 security.txt to static/.well-known/ - #599

Open
kobihikri wants to merge 2 commits into
valkey-io:mainfrom
kobihikri:add-security-txt
Open

Add RFC 9116 security.txt to static/.well-known/#599
kobihikri wants to merge 2 commits into
valkey-io:mainfrom
kobihikri:add-security-txt

Conversation

@kobihikri

Copy link
Copy Markdown

What this does

Adds /.well-known/security.txt to the site by placing it in static/.well-known/security.txt, which Hugo copies verbatim to the published site root.

Why

RFC 9116 defines a small, standard file at /.well-known/security.txt so a security researcher can find where to report a vulnerability without guessing. Right now the live file is missing:

$ curl -sSL -o /dev/null -w '%{http_code}\n' https://valkey.io/.well-known/security.txt
404

The project already has a security contact — I just pointed the file at the existing mailbox and policy rather than inventing anything:

  • Contact: mailto:security@lists.valkey.io (from SECURITY.md in this repo)
  • Policy: https://github.com/valkey-io/valkey/security/policy
  • Expires: — RFC 9116 requires this field; I set it ~1 year out.

Verifying it will serve

static/ is already served verbatim — for example static/img/IconGithub.svg is live at https://valkey.io/img/IconGithub.svg (200). Hugo copies dot-directories under static/ too, so static/.well-known/security.txt will publish at https://valkey.io/.well-known/security.txt. Please double-check on a preview build.

Single file, no other changes. Commit is DCO signed-off.


Disclosure: I used an AI tool to help spot this and draft the file. I verified every line myself against the live site and this repo's SECURITY.md, and I take responsibility for the change. Happy to adjust the Expires date, contact, or policy link to whatever the maintainers prefer.

Adds a machine-discoverable security contact at /.well-known/security.txt,
served by Hugo from static/. Points to the existing security mailbox
(security@lists.valkey.io) and the project's security policy. Includes the
RFC 9116-mandatory Expires field.

Assisted by an AI tool; every line verified by me against the live site and
SECURITY.md.

Signed-off-by: Kobi Hikri <kobi.hikri@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Security.txt Generation

Layer / File(s) Summary
Security.txt generation script
build/init-security-txt.sh
Validates the site root, computes a one-year expiration, creates .well-known, and writes RFC 9116 security metadata.
Deployment wiring and generated-file handling
.github/workflows/zola-deploy.yml, .gitignore
Runs the generator during deployment initialization and ignores the generated security.txt file.

Suggested reviewers: stockholmux, madolson


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a generated RFC 9116 security.txt file to the site build. The main changes are:

  • Adds a build script that writes static/.well-known/security.txt.
  • Runs the script during the Zola deploy workflow.
  • Ignores the generated security.txt file in git.

Confidence Score: 4/5

The generated security contact file needs its policy URL fixed before merging.

The build step writes the expected well-known file location, but the published policy link can still resolve to an indirect GitHub UI page instead of the concrete policy document.

Files Needing Attention: build/init-security-txt.sh

Security Review

The generated Policy field still uses GitHub's /security/policy UI route instead of the concrete security policy document.

T-Rex T-Rex Logs

What T-Rex did

  • Regenerated security.txt after removing the existing file and confirmed the Policy line points to the indirect GitHub policy route.
  • Executed a focused Python assertion against the regenerated security.txt to verify that the Policy line matches the indirect route.
  • Performed HTTP GET requests to the generated Policy URL and the suggested SECURITY.md URL, and both returned 200 OK with distinct final URLs.
  • Ran the security.txt validation workflow to review the end-to-end generation and policy checks using the provided transcripts and scripts.
  • Reviewed the validation artifacts, including the generated security.txt copy and the validation script, which show a successful run with EXIT_CODE 0.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
.github/workflows/zola-deploy.yml Adds the security.txt generation script to the existing site initialization step.
.gitignore Ignores the generated well-known security.txt file.
build/init-security-txt.sh Generates the security.txt file with contact, expiration, canonical, and policy fields.

Reviews (2): Last reviewed commit: "Generate security.txt at build time so E..." | Re-trigger Greptile

Comment thread static/.well-known/security.txt Outdated
Expires: 2027-07-14T00:00:00.000Z
Preferred-Languages: en
Canonical: https://valkey.io/.well-known/security.txt
Policy: https://github.com/valkey-io/valkey/security/policy

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Policy Link Can Miss Document

If GitHub does not expose this repository's security policy through the /security/policy UI route, researchers following the published Policy field can land on a missing or generic page instead of the actual disclosure policy. The site already links its security policy directly to the repository SECURITY.md, so using that same target avoids a broken policy reference in security.txt.

@stockholmux

Copy link
Copy Markdown
Member
  1. This site isn't a Hugo site.
  2. The static expiry seems like a silly thing to just forget to update. Use a template to make sure it always stays 1 year + last site build.

Per review: derive the RFC 9116 Expires field from the build rather than
committing a fixed date, so it always sits one year past the last site build.

- add build/init-security-txt.sh, following the existing build/ script style
- call it from the same workflow step as init-topics-and-clients and
  init-commands
- gitignore the generated file, matching how content/commands and
  content/topics are handled

Signed-off-by: Kobi Hikri <kobi.hikri@gmail.com>
@kobihikri

Copy link
Copy Markdown
Author

Both points taken, @stockholmux — thanks, and you were right on the first one.

1. Not Hugo. My PR description said Hugo twice; this is a Zola site (config.toml with compile_sass/build_search_index/taxonomies, templates/ and sass/ directories, zola-deploy.yml). I checked rather than assumed this time. The static/ passthrough behaviour I relied on happens to be the same in both, so the file would still have served — but I stated the wrong reason, and that's a fair thing to call out.

2. The static expiry. Agreed, and fixed in 8ba6721 — it's now derived from the build instead of committed:

  • build/init-security-txt.sh generates static/.well-known/security.txt with Expires set one year past the build, following the style of the existing scripts in build/.
  • It's invoked from the same workflow step that already runs init-topics-and-clients.sh and init-commands.sh, so it rides along with every build with no new step.
  • The generated file is gitignored, matching how content/commands/* and content/topics/* are handled — so there's no committed date to forget about.

One note on mechanism, since you said "use a template": I looked at doing this as a Zola template first, but Zola only emits templated output at fixed paths it knows about (index, sitemap, robots.txt, feeds) — a page with path = "/.well-known/security.txt" produces \.well-known/security.txt/index.html, not the file itself. Generating before zola build was the closest way to get "1 year + last site build" at the exact path. If you'd rather it lived somewhere else, or want the window to be something other than a year, that's a one-line change.

Also worth flagging honestly: the script branches on GNU vs BSD date, and I could only execute the BSD branch locally. The GNU branch is what CI will actually run, so this PR's build is the real check on it.

Disclosure: I used AI assistance (Claude Opus 5) in preparing this. I verified the generator against this repo's conventions and ran it myself, and I take responsibility for it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@build/init-security-txt.sh`:
- Around line 24-40: Make build/init-security-txt.sh fail immediately when any
generation command fails by enabling strict shell options after argument
validation, including set -euo pipefail. Ensure failures from date, mkdir, or
the security.txt heredoc prevent the script from completing successfully.
- Around line 24-27: Update the date calculation in init-security-txt.sh to use
a fixed interval of 364 days in both the GNU date and BSD date branches,
replacing the current calendar-year shift. Keep the existing UTC timestamp
format and platform detection intact so Expires remains strictly under one year.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 74ec2c39-b2c4-49f3-b90f-17d45144f560

📥 Commits

Reviewing files that changed from the base of the PR and between 66e226d and 8ba6721.

📒 Files selected for processing (3)
  • .github/workflows/zola-deploy.yml
  • .gitignore
  • build/init-security-txt.sh

Comment on lines +24 to +27
if date -u -d '+1 year' >/dev/null 2>&1; then
EXPIRES=$(date -u -d '+1 year' +%Y-%m-%dT%H:%M:%SZ) # GNU date
else
EXPIRES=$(date -u -v+1y +%Y-%m-%dT%H:%M:%SZ) # BSD date

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file context =="
sed -n '1,80p' build/init-security-txt.sh 2>/dev/null || true

echo
echo "== RFC references in repo =="
rg -n "rfc9116|Expires|security.txt|policy.txt" .

echo
echo "== date semantics for one year vs days =="
python3 - <<'PY'
import datetime

for start in ["2023-02-28", "2024-02-28", "2024-12-31", "2025-02-28", "2025-01-01"]:
    d = datetime.date.fromisoformat(start)
    one = d + datetime.timedelta(days=366)
    years = d.replace(year=2026) if d.month == d.month and d.day == d.day else None
    print(f"{start}: +366d={one.isoformat()} days={366}, next-cm=2026-02-28")
PY

tmp="$(mktemp -d)"
if command -v date >/dev/null; then
  echo "== date behavior on this system =="
  printf '%s ' 'GNU/BSD date is:'
  date -u -v+1y +%Y-%m-%dT%H:%M:%SZ >/dev/null 2>&1 && echo "BSD" || echo "not BSD"
  date -u -d '+1 day' +%Y-%m-%dT%H:%M:%SZ >/dev/null 2>&1 && echo "GNU" || echo "not GNU"
  for base in 2023-02-28 2024-02-28 2024-12-31 2025-02-28; do
    d=$(TZ=UTC date -d "$base" +%s)
    echo "----- base $base (epoch $d) -----"
    date -u -d "@$((d + 366*86400))" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || true
    date -u -d "+1 year" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || true
  done
else
  echo "no date command"
fi
rm -rf "$tmp"

Repository: valkey-io/valkey-io.github.io

Length of output: 2976


🌐 Web query:

RFC 9116 security.txt Expires field recommended expiration less than one year

💡 Result:

RFC 9116 explicitly recommends that the "Expires" field in a security.txt file be set to a date and time less than one year in the future [1][2]. This recommendation is intended to help prevent the use of stale information [1][2]. Because inaccurate or outdated security contact information can result in security reports being lost or misdirected—potentially leaving vulnerabilities exposed—the standard emphasizes the importance of keeping the information current [2]. The "Expires" field serves as a mechanism to signal to security researchers when the information in the file is no longer considered valid [1][2]. The field is mandatory and must appear exactly once in the file [1][2].

Citations:


Keep Expires strictly below one year.

RFC 9116 recommends security.txt expirations less than one year in the future. +1 year can be 366 days across a leap year; use a fixed interval such as +364 days instead of a calendar year shift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build/init-security-txt.sh` around lines 24 - 27, Update the date calculation
in init-security-txt.sh to use a fixed interval of 364 days in both the GNU date
and BSD date branches, replacing the current calendar-year shift. Keep the
existing UTC timestamp format and platform detection intact so Expires remains
strictly under one year.

Source: MCP tools

Comment on lines +24 to +40
if date -u -d '+1 year' >/dev/null 2>&1; then
EXPIRES=$(date -u -d '+1 year' +%Y-%m-%dT%H:%M:%SZ) # GNU date
else
EXPIRES=$(date -u -v+1y +%Y-%m-%dT%H:%M:%SZ) # BSD date
fi

mkdir -p "$WELL_KNOWN"

cat > "${WELL_KNOWN}/security.txt" <<EOF
# Valkey security contact — see https://github.com/valkey-io/valkey/security/policy
# Generated at build time by build/init-security-txt.sh — do not edit by hand.
Contact: mailto:security@lists.valkey.io
Expires: ${EXPIRES}
Preferred-Languages: en
Canonical: https://valkey.io/.well-known/security.txt
Policy: https://github.com/valkey-io/valkey/security/policy
EOF

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail the build when generation fails.

Without set -e or explicit status checks, a failed date, mkdir, or cat can be followed by the successful echo, causing .github/workflows/zola-deploy.yml Line 65 to continue with a missing or invalid security.txt.

Add set -euo pipefail after argument validation, or check each command explicitly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@build/init-security-txt.sh` around lines 24 - 40, Make
build/init-security-txt.sh fail immediately when any generation command fails by
enabling strict shell options after argument validation, including set -euo
pipefail. Ensure failures from date, mkdir, or the security.txt heredoc prevent
the script from completing successfully.

Expires: ${EXPIRES}
Preferred-Languages: en
Canonical: https://valkey.io/.well-known/security.txt
Policy: https://github.com/valkey-io/valkey/security/policy

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Policy Route Remains Indirect

The generated security.txt still points Policy at GitHub's /security/policy UI route. If that route is unavailable or renders a generic page instead of the repository policy, researchers following the published policy link can miss the disclosure instructions even though the repository has a concrete SECURITY.md document.

Suggested change
Policy: https://github.com/valkey-io/valkey/security/policy
Policy: https://github.com/valkey-io/valkey/blob/unstable/SECURITY.md
Artifacts

Repro: security.txt generation output showing indirect Policy line

  • The full command output behind this check.

Repro: Policy line verification script

  • Evidence file captured while the check ran.

Repro: Policy line assertion output

  • The full command output behind this check.

Repro: HTTP comparison script for generated and concrete policy URLs

  • Evidence file captured while the check ran.

Repro: HTTP status and response trace for generated and concrete policy URLs

  • The full command output behind this check.

View artifacts

T-Rex Ran code and verified through T-Rex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants