Fix ECDH zeroization, add audit logging, and remediate high findings

- Fix #61: handleRotateKey and handleDeleteUser now zeroize stored
  privBytes instead of calling Bytes() (which returns a copy). New
  state populates privBytes; old references nil'd for GC.
- Add audit logging subsystem (internal/audit) with structured event
  recording for cryptographic operations.
- Add audit log engine spec (engines/auditlog.md).
- Add ValidateName checks across all engines for path traversal (#48).
- Update AUDIT.md: all High findings resolved (0 open).
- Add REMEDIATION.md with detailed remediation tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 14:04:39 -07:00
parent b33d1f99a0
commit 5c5d7e184e
24 changed files with 1699 additions and 72 deletions

View File

@@ -51,7 +51,7 @@ func runInit(cmd *cobra.Command, args []string) error {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
b := barrier.NewAESGCMBarrier(database)
sealMgr := seal.NewManager(database, b, logger)
sealMgr := seal.NewManager(database, b, nil, logger)
if err := sealMgr.CheckInitialized(); err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"fmt"
"log/slog"
"os"
"os/signal"
@@ -10,6 +11,7 @@ import (
mcias "git.wntrmute.dev/kyle/mcias/clients/go"
"github.com/spf13/cobra"
"git.wntrmute.dev/kyle/metacrypt/internal/audit"
"git.wntrmute.dev/kyle/metacrypt/internal/auth"
"git.wntrmute.dev/kyle/metacrypt/internal/barrier"
"git.wntrmute.dev/kyle/metacrypt/internal/config"
@@ -59,8 +61,14 @@ func runServer(cmd *cobra.Command, args []string) error {
return err
}
// Create audit logger.
auditLog, err := createAuditLogger(cfg)
if err != nil {
return err
}
b := barrier.NewAESGCMBarrier(database)
sealMgr := seal.NewManager(database, b, logger)
sealMgr := seal.NewManager(database, b, auditLog, logger)
if err := sealMgr.CheckInitialized(); err != nil {
return err
@@ -81,8 +89,8 @@ func runServer(cmd *cobra.Command, args []string) error {
engineRegistry.RegisterFactory(engine.EngineTypeTransit, transit.NewTransitEngine)
engineRegistry.RegisterFactory(engine.EngineTypeUser, user.NewUserEngine)
srv := server.New(cfg, sealMgr, authenticator, policyEngine, engineRegistry, logger, version)
grpcSrv := grpcserver.New(cfg, sealMgr, authenticator, policyEngine, engineRegistry, logger)
srv := server.New(cfg, sealMgr, authenticator, policyEngine, engineRegistry, auditLog, logger, version)
grpcSrv := grpcserver.New(cfg, sealMgr, authenticator, policyEngine, engineRegistry, auditLog, logger)
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
@@ -106,3 +114,26 @@ func runServer(cmd *cobra.Command, args []string) error {
grpcSrv.Shutdown()
return srv.Shutdown(context.Background())
}
func createAuditLogger(cfg *config.Config) (*audit.Logger, error) {
switch cfg.Audit.Mode {
case "":
return nil, nil // disabled
case "stdout":
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: audit.LevelAudit,
})
return audit.New(h), nil
case "file":
f, err := os.OpenFile(cfg.Audit.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
return nil, fmt.Errorf("audit: open log file %s: %w", cfg.Audit.Path, err)
}
h := slog.NewJSONHandler(f, &slog.HandlerOptions{
Level: audit.LevelAudit,
})
return audit.New(h), nil
default:
return nil, fmt.Errorf("audit: unknown mode %q", cfg.Audit.Mode)
}
}