fix: use hashed webhook secrets for constant-time comparison

Store a SHA-256 hash of the webhook secret in a new webhook_secret_hash
column. FindAppByWebhookSecret now hashes the incoming secret and queries
by hash, eliminating the SQL string comparison timing side-channel.

- Add migration 005_add_webhook_secret_hash.sql
- Add database.HashWebhookSecret() helper
- Backfill existing secrets on startup
- Update App model to include WebhookSecretHash in all queries
- Update app creation to compute hash at insert time
- Add TestHashWebhookSecret unit test
- Update all test fixtures to set WebhookSecretHash

Closes #13
This commit is contained in:
clawbot
2026-02-15 14:06:53 -08:00
parent d4eae284b5
commit 72786a9feb
7 changed files with 117 additions and 27 deletions

View File

@@ -297,6 +297,7 @@ func TestAllApps(t *testing.T) {
app.Branch = testBranch
app.DockerfilePath = "Dockerfile"
app.WebhookSecret = "secret-" + strconv.Itoa(idx)
app.WebhookSecretHash = database.HashWebhookSecret(app.WebhookSecret)
app.SSHPrivateKey = "private"
app.SSHPublicKey = "public"
@@ -791,6 +792,7 @@ func createTestApp(t *testing.T, testDB *database.Database) *models.App {
app.Branch = testBranch
app.DockerfilePath = "Dockerfile"
app.WebhookSecret = "secret-" + t.Name()
app.WebhookSecretHash = database.HashWebhookSecret(app.WebhookSecret)
app.SSHPrivateKey = "private"
app.SSHPublicKey = "public"