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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ red PR job against.

- `scripts/check-links.sh [--external-warn]` — resolves every internal link/anchor and
probes external URLs (CI runs it with `--external-warn` on PRs and pushes; the weekly
scheduled run and manual dispatch hard-fail on broken external links).
scheduled run and manual dispatch hard-fail on broken external links). Besides every
committed page and the `make-redirects.sh` map, it checks the URI fields of
`.well-known/security.txt` (RFC 9116 `Contact`, `Canonical`, `Policy`,
`Acknowledgments`, `Hiring`), so a rotted security-contact link is reported too.
- `scripts/check-links.test.sh` — fixture-based self-test of the link checker; runs
offline (fake `curl`).
- `node --test scripts/story-dialog.test.mjs` — runs the Share-your-story dialog script
Expand Down
11 changes: 11 additions & 0 deletions scripts/check-links.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,23 @@ extract_redirect_targets() {
awk -F'"' '/^[[:space:]]*\[[^]]+\]="/ { split($2, parts, "|"); print "make-redirects.sh\t" parts[1] }' "$redirects"
}

# RFC 9116 §2.5: the values of these security.txt fields are URIs. Comment
# lines (#) and non-URI fields (Expires, Preferred-Languages) are left alone.
extract_security_txt_uris() {
local file=".well-known/security.txt"
[[ -f "$ROOT/$file" ]] || return 0
grep -Ei '^(Contact|Canonical|Policy|Acknowledgments|Hiring):' "$ROOT/$file" |
sed -E -e 's/^[^:]+:[[:space:]]*//' -e 's/[[:space:]]+$//' |
awk -v file="$file" 'NF { print file "\t" $0 }'
}

find "$ROOT" -name '*.html' -not -path "$WORK_DIR/*" -print | sort |
while IFS= read -r file; do
rel="${file#$ROOT/}"
extract_html_links "$rel"
done > "$WORK_DIR/links.tsv"
extract_redirect_targets >> "$WORK_DIR/links.tsv"
extract_security_txt_uris >> "$WORK_DIR/links.tsv"

sort -u "$WORK_DIR/links.tsv" > "$WORK_DIR/links.dedup.tsv"

Expand Down
41 changes: 41 additions & 0 deletions scripts/check-links.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,47 @@ expect_out 'ERROR external broken: make-redirects\.sh -> https://example\.test/b
expect_no_out 'label' "label half is not treated as a URL"
expect_out 'External failures: 1\.' "only the broken target counts"

echo "case: .well-known/security.txt URI fields are checked (RFC 9116 Contact/Canonical/Policy/Acknowledgments/Hiring)"
root="$(new_site security-txt)"
echo '<p>home</p>' > "$root/index.html"
mkdir -p "$root/.well-known"
cat > "$root/.well-known/security.txt" <<'TXT'
# A comment mentioning https://example.test/broken-comment must be ignored
Contact: mailto:security@example.test
Contact: https://example.test/broken
Expires: 2027-01-01T00:00:00.000Z
Preferred-Languages: en
Canonical: https://hivecommons.dev/.well-known/security.txt
Policy: https://example.test/fine
Acknowledgments: https://example.test/soft404
Hiring: https://example.test/server-error
TXT
run_checker "$root"
expect_rc 1 "rotten security.txt URIs fail the gate"
expect_out 'ERROR external broken: \.well-known/security\.txt -> https://example\.test/broken \(404' "https Contact is probed and attributed to security.txt"
expect_out 'ERROR external soft-404: \.well-known/security\.txt -> https://example\.test/soft404 \(200' "Acknowledgments is probed"
expect_out 'ERROR external broken: \.well-known/security\.txt -> https://example\.test/server-error \(500' "Hiring is probed"
expect_no_out 'broken-comment' "comment lines are not harvested"
expect_no_out 'mailto' "mailto: Contact is skipped, not flagged"
expect_no_out 'internal target missing' "Canonical resolves to the committed security.txt"
expect_out 'External failures: 3\.' "Policy that resolves is not counted"

echo "case: security.txt Canonical pointing at a path the tree does not publish is an internal error"
root="$(new_site security-canonical)"
echo '<p>home</p>' > "$root/index.html"
mkdir -p "$root/.well-known"
printf 'Contact: mailto:security@example.test\nCanonical: https://hivecommons.dev/.well-known/other.txt\n' > "$root/.well-known/security.txt"
run_checker "$root" --external-warn
expect_rc 1 "unpublished Canonical fails even under --external-warn"
expect_out 'ERROR internal target missing: \.well-known/security\.txt -> https://hivecommons\.dev/\.well-known/other\.txt' "Canonical is resolved against the tree like any site URL"

echo "case: a site without security.txt is unaffected"
root="$(new_site no-security-txt)"
echo '<a href="/">home</a>' > "$root/index.html"
run_checker "$root"
expect_rc 0 "no security.txt, no extra links"
expect_out 'Checked 1 unique links\.' "only the page link is counted"

echo "case: external failures — hard 404, 5xx, curl failure, soft-404"
root="$(new_site external)"
cat > "$root/index.html" <<'HTML'
Expand Down
Loading