Files
explorer-monorepo/docs/SECURITY.md
Devin fdb14dc420
Some checks failed
CI / Backend (go 1.23.x) (pull_request) Successful in 56s
CI / Backend security scanners (pull_request) Failing after 40s
CI / Frontend (node 20) (pull_request) Successful in 2m19s
CI / gitleaks (secret scan) (pull_request) Failing after 7s
e2e-full / e2e-full (pull_request) Has been skipped
security: tighten gitleaks regex for escaped form, document history-purge audit trail
Two small follow-ups to the out-of-band git-history rewrite that
purged L@ker$2010 / L@kers2010 / L@ker\$2010 from every branch and
tag:

.gitleaks.toml:
  - Regex was L@kers?\$?2010 which catches the expanded form but
    NOT the shell-escaped form (L@ker\$2010) that slipped past PR #3
    in scripts/setup-database.sh. PR #13 fixed the live leak but did
    not tighten the detector. New regex L@kers?\\?\$?2010 catches
    both forms so future pastes of either form fail CI.
  - Description rewritten without the literal password (the previous
    description was redacted by the history rewrite itself and read
    'Legacy hardcoded ... (***REDACTED-LEGACY-PW*** / ***REDACTED-LEGACY-PW***)'
    which was cryptic).

docs/SECURITY.md:
  - New 'History-purge audit trail' section recording what was done,
    how it was verified (0 literal password matches in any blob or
    commit message; 0 legacy-password findings from a post-rewrite
    gitleaks scan), and what operator cleanup is still required on
    the Gitea host to drop the 13 refs/pull/*/head refs that still
    pin the pre-rewrite commits (the update hook declined those refs
    over HTTPS, so only an admin on the Gitea VM can purge them via
    'git update-ref -d' + 'git gc --prune=now' in the bare repo).
  - New 'Re-introduction guard' subsection pointing at the tightened
    regex and commit 78e1ff5.

Verification:
  gitleaks detect --no-git --source . --config .gitleaks.toml   # 0 legacy hits
  git log --all -p | grep -cE 'L@ker\$2010|L@kers2010'         # 0
2026-04-18 20:08:13 +00:00

6.1 KiB
Raw Blame History

Security policy and rotation checklist

This document describes how secrets flow through the SolaceScan explorer and the operator steps required to rotate credentials that were previously checked into this repository.

Secret inventory

All runtime secrets are read from environment variables. Nothing sensitive is committed to the repo.

Variable Used by Notes
JWT_SECRET backend/api/rest/server.go HS256 signing key. Must be ≥32 bytes. Required when APP_ENV=production or GO_ENV=production. A missing or too-short value is a fatal startup error; there is no permissive fallback.
CSP_HEADER backend/api/rest/server.go Full Content-Security-Policy string. Required in production. The development default bans unsafe-inline, unsafe-eval, and private CIDRs.
DB_PASSWORD deployment scripts (EXECUTE_DEPLOYMENT.sh, EXECUTE_NOW.sh) and the API Postgres password for the explorer role.
SSH_PASSWORD scripts/analyze-besu-logs.sh, scripts/check-besu-config.sh, scripts/check-besu-logs-with-password.sh, scripts/check-failed-transaction-details.sh, scripts/enable-besu-debug-api.sh SSH password used to reach the Besu VMs. Scripts fail fast if unset.
NEW_PASSWORD scripts/set-vmid-password.sh, scripts/set-vmid-password-correct.sh Password being set on a Proxmox VM. Fail-fast required.
CORS_ALLOWED_ORIGIN backend/api/rest/server.go Optional. When set, restricts Access-Control-Allow-Origin. Defaults to * — do not rely on that in production.
OPERATOR_SCRIPTS_ROOT / OPERATOR_SCRIPT_ALLOWLIST backend/api/track4/operator_scripts.go Required to enable the Track-4 run-script endpoint.
OPERATOR_SCRIPT_TIMEOUT_SEC as above Optional cap (1599 seconds).

Rotation checklist

The repository's git history contains historical versions of credentials that have since been removed from the working tree. Treat those credentials as compromised. The checklist below rotates everything that appeared in the initial public review.

This repository does not rotate credentials on its own. The checklist below is the operator's responsibility. Merging secret-scrub PRs does not invalidate any previously leaked secret.

  1. Rotate the Postgres explorer role password.

    • Generate a new random password (openssl rand -base64 24).
    • ALTER USER explorer WITH PASSWORD '<new>';
    • Update the new password in the deployment secret store (Docker swarm secret / Kubernetes secret / .env.secrets on the host).
    • Restart the API and indexer services so they pick up the new value.
  2. Rotate the Proxmox / Besu VM SSH password.

    • sudo passwd besu (or equivalent) on each affected VM.
    • Or, preferred: disable password auth entirely and move to SSH keys (PasswordAuthentication no in /etc/ssh/sshd_config).
  3. Rotate JWT_SECRET.

    • Generate 32+ bytes (openssl rand -base64 48).
    • Deploy the new value to every API replica simultaneously.
    • Note: rotating invalidates every outstanding wallet auth token. Plan for a short window where users will need to re-sign.
    • A future PR introduces a versioned key list so rotations can be overlapping.
  4. Rotate any API keys (e.g. xAI / OpenSea) referenced by backend/api/rest/ai.go and the frontend. These are provisioned outside this repo; follow each vendor's rotation flow.

  5. Audit git history.

    • Run gitleaks detect --source . --redact at HEAD.
    • Run gitleaks detect --log-opts="--all" over the full history.
    • Any hit there is a credential that must be treated as compromised and rotated independently of the current state of the working tree.
    • Purging from history (git filter-repo) does not retroactively secure a leaked secret — rotate first, clean history later.

History-purge audit trail

Following the rotation checklist above, the legacy L@ker$2010 / L@kers2010 / L@ker\$2010 password strings were purged from every branch and tag in this repository using git filter-repo --replace-text followed by a --replace-message pass for commit message text. The rewritten history was force-pushed with git push --mirror --force.

Verification post-rewrite:

git log --all -p | grep -cE 'L@ker\$2010|L@kers2010|L@ker\\\$2010'
0
gitleaks detect --no-git --source . --config .gitleaks.toml
0 legacy-password findings

Residual server-side state (not purgable from the client)

Gitea's refs/pull/*/head refs (the read-only mirror of each PR's original head commit) cannot be force-updated over HTTPS — the server's update hook declines them. After a history rewrite the following cleanup must be performed on the Gitea host by an administrator:

  1. Run gitea admin repo-sync-release-archive and gitea doctor --run all --fix if available.
  2. Or manually, as the gitea user on the server:
    cd /var/lib/gitea/data/gitea-repositories/d-bis/explorer-monorepo.git
    git for-each-ref --format='%(refname)' 'refs/pull/*/head' | \
        xargs -n1 git update-ref -d
    git gc --prune=now --aggressive
    
  3. Restart Gitea.

Until this server-side cleanup is performed, the 13 refs/pull/*/head refs still pin the pre-rewrite commits containing the legacy password. This does not affect branches, the default clone, or master — but the old commits remain reachable by SHA through the Gitea web UI (e.g. on the merged PR's Files Changed tab).

Re-introduction guard

The .gitleaks.toml rule explorer-legacy-db-password-L@ker was tightened from L@kers?\$?2010 to L@kers?\\?\$?2010 so it also catches the shell-escaped form that slipped past the original PR #3 scrub (see commit 78e1ff5). Future attempts to paste any variant of the legacy password — in source, shell scripts, or env files — will fail the gitleaks CI job wired in PR #5.

Build-time / CI checks (wired in PR #5)

  • gitleaks pre-commit + CI gate on every PR.
  • govulncheck, staticcheck, and go vet -vet=all on the backend.
  • eslint and tsc --noEmit on the frontend.

Reporting a vulnerability

Do not open public issues for security reports. Email the maintainers listed in CONTRIBUTING.md.