security.txt Expiry, Encryption-Key Resolvability avatar

security.txt Expiry, Encryption-Key Resolvability

Pricing

$5.00 / 1,000 domain checkeds

Go to Apify Store
security.txt Expiry, Encryption-Key Resolvability

security.txt Expiry, Encryption-Key Resolvability

Checks security.txt files for expiry, unresolvable OpenPGP keys and unreachable contacts. Keeps a ledger of changes between runs.

Pricing

$5.00 / 1,000 domain checkeds

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 hours ago

Last modified

Categories

Share

security.txt Expiry, Encryption-Key Resolvability, and Contact Reachability Ledger

This Actor checks the RFC 9116 security.txt file of each domain you give it. It tells you before a reporter finds out that a file has expired, that a listed OpenPGP key cannot be fetched, or that a Contact URL is not reachable. It keeps a ledger between runs, so it also reports what changed.

What the Actor does for each domain

  1. It fetches https://<domain>/.well-known/security.txt. If that fails, it fetches /security.txt.
  2. It reads Contact, Expires, Encryption, and Canonical. It accepts PGP-signed files.
  3. It finds up to 3 Encryption keys. A key is an openpgp4fpr: fingerprint or a URL. For a URL, the Actor downloads the key and reads its fingerprint and expiry. Then it asks each selected keyserver for that fingerprint.
  4. If checkContactUrls is true, it sends a HEAD request to up to 2 https: Contact values. It does not check mailto: or tel: values. A status below 400 means reachable. Some servers refuse HEAD requests. They show as not reachable.
  5. It compares the result with the state from the last run and stores the new state.

Input

FieldDefaultMeaning
domains["curl.se", "github.com"]1 to 1000 hostnames.
expiryLeadDays30Status is due when Expires is less than this number of days away.
keyserverskeys.openpgp.org, keyserver.ubuntu.comKeyservers to ask.
checkContactUrlstrueHEAD check for https: Contact values.
stateStoreNamestx-key-ledgerNamed key-value store for ledger state.

An empty input {} works. Every field has a default.

Output

One dataset item per domain. Main fields:

  • servedLocation, httpStatus, contentType, isPgpSigned
  • expires, daysToExpiry, expiryStatus (ok, due, expired, or missing)
  • canonicalMatchesServedUrl (null when the file has no Canonical)
  • contacts[] with reachable (null when not checked)
  • encryptionKeys[] with source (URL or fingerprint), fingerprint, resolvedOn[], keyExpiry, reason (unsupported_scheme when the value is not an openpgp4fpr: or http(s):// value, for example a dns: URI; source is URL for these values and they show as not resolved)
  • resolvedOn[] and keyExpiry at top level (union of servers, first key expiry found)
  • changeEvents[]: expires_moved, key_added, key_removed, contact_changed, file_removed
  • alerts[]: expiry_due, expiry_expired, expires_missing, file_missing, key_unresolved, key_expired, contact_unreachable
  • error: a reason code when the check could not finish

The first run for a domain has no changeEvents, because no earlier state exists.

The run also writes a SUMMARY record to the default key-value store. It has counts by expiryStatus, alert count, change-event count, and charge data.

A failed check is a normal result. A missing file, an expired file, or an unreachable target does not fail the run. The run fails only on a real malfunction.

Pricing event

The pricing model is pay-per-event. The Actor calls Actor.charge once per domain with this event:

Event nameUnitPlanned price
domain-checkedOne domain: security.txt fetched, keys resolved, and ledger updated.$0.005

The Actor does not charge for an invalid hostname, for a private or reserved target, or for a domain that does not resolve in DNS. If the charge limit is reached, the Actor stops and reports the skipped domains in SUMMARY. You set the price in Apify Console. This repository does not change pricing.

Safety limits

  • HTTP only. No browser, proxy, LLM, paid API, or secret.
  • Only https: on port 443. Private, loopback, link-local, and reserved addresses are rejected. The Actor checks every redirect target too.
  • At most 3 redirects, 64 KB per response, 10 s per request, 30 s per fetch, 5 domains at once.
  • Known limit: the Actor resolves a hostname before it connects. It does not pin the address. A hostile DNS server could still change the answer between the two steps.
  • Known limit: key downloads stop at 1 MiB. A larger key shows as not resolved.
  • Known limit: the OpenPGP reader supports v4 keys only. Other key versions show as not resolved.

Development

uv run pytest
uv run ruff check .