Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,17 @@ jobs:
-server internal/server/server.go \
-update
if ! git diff --quiet internal/handler/spec/openapi.json; then
# On branch pushes, self-heal by committing the scaffolded stubs back.
# On tag builds GITHUB_REF_NAME is the tag, so a push here targets a
# tag ref and fails (git exit 128); skip it — the spec was already
# validated on the branch push that produced this commit.
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
# Self-heal only makes sense on a push to a real branch: GITHUB_REF_NAME
# is a pushable ref there. On pull_request events GITHUB_REF_NAME is the
# synthetic "<pr>/merge" ref (detached HEAD, not a branch on origin), so
# `git pull --rebase origin "<pr>/merge"` fails with "couldn't find
# remote ref" — fail loudly instead and ask the author to commit the
# stub. On tag builds, the spec was already validated on the branch
# push that produced this commit, so just skip.
if [ "${GITHUB_EVENT_NAME}" != "push" ]; then
echo "::error::openapi spec drift detected. Run 'go run ./tools/speccheck -spec internal/handler/spec/openapi.json -server internal/server/server.go -update' locally and commit internal/handler/spec/openapi.json."
exit 1
elif [ "${GITHUB_REF_TYPE}" = "tag" ]; then
echo "openapi spec drift on a tag build; not pushing (validated on branch)."
else
git config user.name "github-actions[bot]"
Expand Down
36 changes: 30 additions & 6 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@tailwindcss/postcss": "^4.3.0",
"@tanstack/react-query": "^5.56.0",
"@tanstack/react-query-devtools": "^5.56.0",
"axios": "^1.7.7",
"axios": "^1.18.1",
"clsx": "^2.1.1",
"lucide-react": "^1.8.0",
"react": "^19.2.6",
Expand Down
8 changes: 8 additions & 0 deletions internal/handler/admin_api_keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/clavex-eu/clavex/internal/models"
"github.com/clavex-eu/clavex/internal/repository"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -110,6 +111,13 @@ func apiKeyTestPool(t *testing.T) *pgxpool.Pool {
}
cfg, err := pgxpool.ParseConfig(dsn)
require.NoError(t, err)
// Mirror the app pool: migration 000017 moves tables into the identity /
// sessions / audit schemas, so without this search_path queries against a
// raw DSN see only `public` and fail with "relation does not exist".
cfg.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
_, err := conn.Exec(ctx, `SET search_path = identity, sessions, audit, public`)
return err
}
pool, err := pgxpool.NewWithConfig(context.Background(), cfg)
require.NoError(t, err)
t.Cleanup(pool.Close)
Expand Down
8 changes: 8 additions & 0 deletions internal/handler/admin_delegation_escalation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/clavex-eu/clavex/internal/models"
"github.com/clavex-eu/clavex/internal/repository"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -118,6 +119,13 @@ func roleTestPool(t *testing.T) *pgxpool.Pool {
}
cfg, err := pgxpool.ParseConfig(dsn)
require.NoError(t, err)
// Mirror the app pool: migration 000017 moves tables into the identity /
// sessions / audit schemas, so without this search_path queries against a
// raw DSN see only `public` and fail with "relation does not exist".
cfg.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
_, err := conn.Exec(ctx, `SET search_path = identity, sessions, audit, public`)
return err
}
pool, err := pgxpool.NewWithConfig(context.Background(), cfg)
require.NoError(t, err)
t.Cleanup(pool.Close)
Expand Down
6 changes: 6 additions & 0 deletions internal/repository/admin_api_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ func generateKey() (string, error) {
return keyPrefix + base64.RawURLEncoding.EncodeToString(b), nil
}

// hashKey stores a lookup hash of rawKey. SHA-256 is appropriate here — unlike
// a password, rawKey is a 256-bit value from crypto/rand (see generateKey), so
// it carries no exploitable entropy for an offline brute-force/rainbow-table
// attack; a computationally-expensive password hash (bcrypt/argon2/PBKDF2)
// would add cost with no security benefit for a token this size.
// lgtm[go/weak-sensitive-data-hashing]
func hashKey(rawKey string) string {
h := sha256.Sum256([]byte(rawKey))
return hex.EncodeToString(h[:])
Expand Down
Loading