fix(01-foundation): address all checker blockers and warnings in phase plans

This commit is contained in:
salvacybersec
2026-04-04 23:57:01 +03:00
parent 684b67cb73
commit fb8a1f002b
4 changed files with 337 additions and 154 deletions

View File

@@ -20,7 +20,7 @@ must_haves:
- "AES-256-GCM Encrypt/Decrypt roundtrip produces the original plaintext"
- "Argon2id DeriveKey with the same passphrase and salt always returns the same 32-byte key"
- "A Finding can be saved to the database with the key_value stored encrypted and retrieved as plaintext"
- "The raw database file does NOT contain plaintext API key values"
- "The raw database file does NOT contain plaintext API key values — verified by querying raw bytes from the BLOB column"
artifacts:
- path: "pkg/storage/encrypt.go"
provides: "Encrypt(plaintext, key) and Decrypt(ciphertext, key) using AES-256-GCM"
@@ -271,7 +271,7 @@ func NewSalt() ([]byte, error) {
- Test 3: DeriveKey(passphrase, salt) twice returns identical 32 bytes
- Test 4: NewSalt() twice returns different slices
- Test 5: SaveFinding stores finding → ListFindings decrypts and returns KeyValue == "sk-proj-test"
- Test 6: Database file (when not :memory:) does NOT contain literal "sk-proj-test" in raw bytes
- Test 6: Raw BLOB bytes retrieved directly from the database do NOT contain the plaintext key string
</behavior>
<action>
Create **pkg/storage/schema.sql**:
@@ -473,6 +473,7 @@ Fill **pkg/storage/db_test.go** (replacing stubs from Plan 01):
package storage_test
import (
"bytes"
"testing"
"github.com/salvacybersec/keyhunter/pkg/storage"
@@ -567,9 +568,10 @@ func TestSaveFindingEncrypted(t *testing.T) {
// Derive a test key
key := storage.DeriveKey([]byte("testpassphrase"), []byte("testsalt1234xxxx"))
plainKey := "sk-proj-test1234567890abcdefghijklmnopqr"
f := storage.Finding{
ProviderName: "openai",
KeyValue: "sk-proj-test1234567890abcdefghijklmnopqr",
KeyValue: plainKey,
Confidence: "high",
SourcePath: "/test/file.env",
SourceType: "file",
@@ -583,10 +585,17 @@ func TestSaveFindingEncrypted(t *testing.T) {
findings, err := db.ListFindings(key)
require.NoError(t, err)
require.Len(t, findings, 1)
assert.Equal(t, "sk-proj-test1234567890abcdefghijklmnopqr", findings[0].KeyValue)
assert.Equal(t, plainKey, findings[0].KeyValue)
assert.Equal(t, "openai", findings[0].ProviderName)
// Verify masking
assert.Equal(t, "sk-proj-...opqr", findings[0].KeyMasked)
// Verify encryption contract: raw BLOB bytes in the database must NOT contain the plaintext key.
// This confirms Encrypt() was called before INSERT, not that the key was stored verbatim.
var rawBlob []byte
require.NoError(t, db.SQL().QueryRow("SELECT key_value FROM findings WHERE id = ?", id).Scan(&rawBlob))
assert.False(t, bytes.Contains(rawBlob, []byte(plainKey)),
"raw database BLOB must not contain plaintext key — encryption was not applied")
}
```
</action>
@@ -601,12 +610,12 @@ func TestSaveFindingEncrypted(t *testing.T) {
- TestDecryptWrongKey passes — wrong key causes error
- TestArgon2KeyDerivation passes — 32 bytes, deterministic
- TestNewSalt passes — 16 bytes, non-deterministic
- TestSaveFindingEncrypted passes — stored and retrieved with correct KeyValue and KeyMasked
- TestSaveFindingEncrypted passes — stored and retrieved with correct KeyValue, KeyMasked, AND raw BLOB does not contain plaintext
- `grep -q 'go:embed.*schema' pkg/storage/db.go` exits 0
- `grep -q 'modernc.org/sqlite' pkg/storage/db.go` exits 0
- `grep -q 'journal_mode=WAL' pkg/storage/db.go` exits 0
</acceptance_criteria>
<done>Storage layer complete — SQLite opens with schema, AES-256-GCM encrypt/decrypt works, Argon2id key derivation works, SaveFinding/ListFindings encrypt/decrypt transparently. All 7 tests pass.</done>
<done>Storage layer complete — SQLite opens with schema, AES-256-GCM encrypt/decrypt works, Argon2id key derivation works, SaveFinding/ListFindings encrypt/decrypt transparently. Raw BLOB bytes verified to not contain plaintext. All 7 tests pass.</done>
</task>
</tasks>
@@ -619,6 +628,7 @@ After both tasks:
- `grep -q 'cipher\.NewGCM' pkg/storage/encrypt.go` exits 0
- `grep -q 'journal_mode=WAL' pkg/storage/db.go` exits 0
- schema.sql contains CREATE TABLE for findings, scans, settings
- TestSaveFindingEncrypted asserts raw BLOB does not contain plaintext key
</verification>
<success_criteria>
@@ -626,6 +636,7 @@ After both tasks:
- AES-256-GCM column encryption works: Encrypt + Decrypt roundtrip returns original (STOR-02)
- Argon2id key derivation: DeriveKey deterministic, 32 bytes, RFC 9106 params (STOR-03)
- FindingCRUD: SaveFinding encrypts before INSERT, ListFindings decrypts after SELECT
- Raw BLOB in database does not contain plaintext key — verified by automated test
- All 7 storage tests pass
</success_criteria>