Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
95c5a59
chore: stage security hardening patch
park285 Jul 15, 2026
d445aa5
chore: run verified security hardening patch
park285 Jul 15, 2026
e2edf0b
chore: allow PR-triggered security patch verification
park285 Jul 15, 2026
24cc368
chore: persist security patch test diagnostics
park285 Jul 15, 2026
fed2685
chore: record security patch test failure
github-actions[bot] Jul 15, 2026
ead41b1
chore: record security patch test failure
github-actions[bot] Jul 15, 2026
0472b98
fix: preserve value-aware generic key redaction
park285 Jul 15, 2026
251c0e5
chore: serialize verified security patch execution
park285 Jul 15, 2026
07b27f1
fix: separate verified code commit from workflow updates
park285 Jul 15, 2026
b10c7e4
security: harden shared library boundaries
github-actions[bot] Jul 15, 2026
d9d5f9c
ci: run workflow policy regression tests
park285 Jul 15, 2026
dda40bd
chore: remove one-shot security patch workflow
park285 Jul 15, 2026
7712016
chore: record security patch test failure
github-actions[bot] Jul 15, 2026
67a4141
chore: remove temporary security diagnostics
park285 Jul 15, 2026
e969c55
chore: verify generic key literal redaction
park285 Jul 15, 2026
a9815a1
security: redact literal secret values in generic key fields
park285 Jul 15, 2026
e30dcba
test: cover literal secret values in generic key fields
park285 Jul 15, 2026
a1c450a
chore: remove temporary secret literal workflow
park285 Jul 15, 2026
5fac8b2
chore: add temporary lint diagnostics
park285 Jul 15, 2026
a2fed7e
chore: record lint diagnostics
github-actions[bot] Jul 15, 2026
cc3bc54
ci: apply and verify shared-go lint fix
park285 Jul 15, 2026
595572f
fix(logging): reuse secret key constants
github-actions[bot] Jul 15, 2026
e1bdf10
test: cover normalized literal secret values
park285 Jul 15, 2026
b361865
test(ci): run workflow policy regression suite from Go tests
park285 Jul 15, 2026
ca55b61
ci: keep workflow definition unchanged
park285 Jul 15, 2026
ae5c73b
test(ci): bind policy regression process to test context
park285 Jul 15, 2026
f6e90f9
test(ci): satisfy external parallel test policy
park285 Jul 15, 2026
49e0b91
chore: capture final lint diagnostics
park285 Jul 15, 2026
59e4fe9
chore: record final lint diagnostics
github-actions[bot] Jul 15, 2026
40b453a
fix(logging): reuse secret key constant
park285 Jul 15, 2026
1fcd93d
test(logging): reuse secret key constant
park285 Jul 15, 2026
0af5a3f
chore: record final lint diagnostics
github-actions[bot] Jul 15, 2026
5dc80a3
fix(logging): restore sanitize handler declaration
park285 Jul 15, 2026
20f4d52
chore: record final lint diagnostics
github-actions[bot] Jul 15, 2026
cea25b2
chore: remove successful lint diagnostics
park285 Jul 15, 2026
94e9ce4
chore: remove lint status diagnostics
park285 Jul 15, 2026
d2e6842
chore: remove temporary lint workflow
park285 Jul 15, 2026
010b4ad
perf(promptguard): raw literal prefilter 보안 의미 보존
park285 Jul 15, 2026
969a714
Merge remote-tracking branch 'origin/main' into update/shared-securit…
park285 Jul 15, 2026
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
24 changes: 24 additions & 0 deletions internal/cipolicy/workflow_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cipolicy_test

import (
"os/exec"
"path/filepath"
"runtime"
"testing"
)

func TestWorkflowPolicyRegressionSuite(t *testing.T) {
t.Parallel()

_, file, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("resolve test source path")
}
repoRoot := filepath.Clean(filepath.Join(filepath.Dir(file), "..", ".."))
cmd := exec.CommandContext(t.Context(), "bash", "scripts/ci/check-workflow-secrets_test.sh")
cmd.Dir = repoRoot
output, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("workflow policy regression suite failed: %v\n%s", err, output)
}
}
53 changes: 41 additions & 12 deletions pkg/logging/sanitize.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
// Copyright (c) 2025 Kapu
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package logging

import (
Expand All @@ -7,15 +27,6 @@ import (
"strings"
)

type sanitizeHandler struct {
inner slog.Handler
}

func newSanitizeHandler(inner slog.Handler) *sanitizeHandler {
return &sanitizeHandler{inner: inner}
}

// 민감 키 리스트 (case-insensitive 매칭)
var (
bearerTokenRegex = regexp.MustCompile(`(?i)\b(bearer\s+)[A-Za-z0-9._~+/=-]+`)
querySecretRegex = regexp.MustCompile(`(?i)([?&;](?:key|api_key|apikey|token|password|pwd|passwd|client_secret|secret|private_key|secret_key)=)[^&\s]+`)
Expand All @@ -24,14 +35,16 @@ var (
const (
tokenAPIKey = "api_key"
tokenAPIKeyCompact = "apikey"
tokenPrivateKey = "private_key"
tokenSecretKey = "secret_key"
)

// querySecretTokens는 querySecretRegex가 매치할 수 있는 키 이름 집합으로,
// 정규식 실행 전 싼 substring pre-check 게이트에 쓰인다. 정규식이 매치하는
// 입력은 반드시 이 토큰 중 하나를 case-insensitive로 포함하므로 게이트는 안전하다.
var querySecretTokens = []string{
"key", tokenAPIKey, tokenAPIKeyCompact, "token", "password", "pwd", "passwd",
"client_secret", "secret", "private_key", "secret_key",
"client_secret", "secret", tokenPrivateKey, tokenSecretKey,
}

var sensitiveExactKeys = map[string]struct{}{
Expand All @@ -46,8 +59,8 @@ var sensitiveExactKeys = map[string]struct{}{
"client_secret": {},
tokenAPIKey: {},
tokenAPIKeyCompact: {},
"private_key": {},
"secret_key": {},
tokenPrivateKey: {},
tokenSecretKey: {},
"authorization": {},
"auth_header": {},
"cookie": {},
Expand Down Expand Up @@ -248,9 +261,25 @@ var secretLikePrefixes = []string{
"eyj",
}

var secretLikeExactValues = map[string]struct{}{
"access-token": {},
"access_token": {},
"api-key": {},
tokenAPIKey: {},
"private-key": {},
tokenPrivateKey: {},
"refresh-token": {},
"refresh_token": {},
"secret-key": {},
tokenSecretKey: {},
}

const secretLikeMinLen = 24

func isSecretLikeValue(v string) bool {
if _, ok := secretLikeExactValues[strings.ToLower(strings.TrimSpace(v))]; ok {
return true
}
if hasSecretLikePrefix(v) {
return true
}
Expand Down
11 changes: 11 additions & 0 deletions pkg/logging/sanitize_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package logging

import "log/slog"

type sanitizeHandler struct {
inner slog.Handler
}

func newSanitizeHandler(inner slog.Handler) *sanitizeHandler {
return &sanitizeHandler{inner: inner}
}
38 changes: 38 additions & 0 deletions pkg/logging/sanitize_literal_secret_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package logging

import (
"strings"
"testing"
)

func TestSanitizeKey_LiteralSecretNamesAreMasked(t *testing.T) {
t.Parallel()

for _, value := range []string{
"secret-key",
tokenSecretKey,
"api-key",
tokenAPIKey,
"private-key",
tokenPrivateKey,
"access-token",
"access_token",
"refresh-token",
"refresh_token",
" SECRET-KEY ",
"Api_Key",
} {
value := value
t.Run(value, func(t *testing.T) {
t.Parallel()

output := keyFieldOutput(t, "key", value)
if strings.Contains(output, value) {
t.Fatalf("literal secret value %q leaked in output: %s", value, output)
}
if !strings.Contains(output, "***REDACTED***") {
t.Fatalf("literal secret value %q was not redacted: %s", value, output)
}
})
}
}
Loading