135 lines
3.8 KiB
Diff
135 lines
3.8 KiB
Diff
--- a/models/db/engine.go
|
|
+++ b/models/db/engine.go
|
|
@@ -330,8 +330,12 @@
|
|
func (TracingHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
|
|
ctx, task := trace.NewTask(c.Ctx, "sql")
|
|
ctx = context.WithValue(ctx, sqlTask{}, task)
|
|
- trace.Log(ctx, "query", c.SQL)
|
|
- trace.Logf(ctx, "args", "%v", c.Args)
|
|
+ if isSensitiveSQL(c.Ctx) {
|
|
+ trace.Log(ctx, "query", "[owner-confined query]")
|
|
+ } else {
|
|
+ trace.Log(ctx, "query", c.SQL)
|
|
+ trace.Logf(ctx, "args", "%v", c.Args)
|
|
+ }
|
|
return ctx, nil
|
|
}
|
|
|
|
@@ -353,7 +357,11 @@
|
|
|
|
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
|
if c.ExecuteTime >= h.Treshold {
|
|
- h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
|
+ if isSensitiveSQL(c.Ctx) {
|
|
+ h.Logger.Log(8, log.WARN, "[Slow SQL Query] [owner-confined query] - %v", c.ExecuteTime)
|
|
+ } else {
|
|
+ h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
|
|
+ }
|
|
}
|
|
return nil
|
|
}
|
|
@@ -370,7 +378,11 @@
|
|
|
|
func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error {
|
|
if c.Err != nil && !errors.Is(c.Err, context.Canceled) {
|
|
- h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err)
|
|
- }
|
|
- return nil
|
|
-}
|
|
+ if isSensitiveSQL(c.Ctx) {
|
|
+ h.Logger.Log(8, log.ERROR, "[Error SQL Query] [owner-confined query]")
|
|
+ } else {
|
|
+ h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err)
|
|
+ }
|
|
+ }
|
|
+ return nil
|
|
+}
|
|
--- /dev/null
|
|
+++ b/models/db/sensitive_sql.go
|
|
@@ -0,0 +1,18 @@
|
|
+// Copyright 2026 The Forgejo Authors. All rights reserved.
|
|
+// SPDX-License-Identifier: MIT
|
|
+package db
|
|
+
|
|
+import "context"
|
|
+
|
|
+type sensitiveSQLKey struct{}
|
|
+
|
|
+// WithSensitiveSQL suppresses argument, statement and driver-error disclosure
|
|
+// in the application's query hooks. The caller must separately refuse raw XORM
|
|
+// SQL logging; custom driver/server auditing remains an owner custody concern.
|
|
+func WithSensitiveSQL(ctx context.Context) context.Context {
|
|
+ return context.WithValue(ctx, sensitiveSQLKey{}, true)
|
|
+}
|
|
+func isSensitiveSQL(ctx context.Context) bool {
|
|
+ sensitive, _ := ctx.Value(sensitiveSQLKey{}).(bool)
|
|
+ return sensitive
|
|
+}
|
|
--- /dev/null
|
|
+++ b/models/db/sensitive_sql_test.go
|
|
@@ -0,0 +1,65 @@
|
|
+// Copyright 2026 The Forgejo Authors. All rights reserved.
|
|
+// SPDX-License-Identifier: MIT
|
|
+package db
|
|
+
|
|
+import (
|
|
+ "bytes"
|
|
+ "context"
|
|
+ "errors"
|
|
+ "fmt"
|
|
+ "runtime/trace"
|
|
+ "strings"
|
|
+ "testing"
|
|
+ "time"
|
|
+
|
|
+ "forgejo.org/modules/log"
|
|
+ "xorm.io/xorm/contexts"
|
|
+)
|
|
+
|
|
+type sensitiveSQLLog struct {
|
|
+ log.Logger
|
|
+ entries []string
|
|
+}
|
|
+
|
|
+func (l *sensitiveSQLLog) Log(_ int, _ log.Level, f string, v ...any) {
|
|
+ l.entries = append(l.entries, fmt.Sprintf(f, v...))
|
|
+}
|
|
+func TestSensitiveSQLHooks(t *testing.T) {
|
|
+ const secret = "synthetic-query-private-sentinel"
|
|
+ for _, sensitive := range []bool{false, true} {
|
|
+ ctx := context.Background()
|
|
+ if sensitive {
|
|
+ ctx = WithSensitiveSQL(ctx)
|
|
+ }
|
|
+ h := &contexts.ContextHook{Ctx: ctx, SQL: "SELECT '" + secret + "'", Args: []any{secret}, Err: errors.New(secret), ExecuteTime: time.Second}
|
|
+ logger := new(sensitiveSQLLog)
|
|
+ if err := (&SlowQueryHook{Logger: logger}).AfterProcess(h); err != nil {
|
|
+ t.Fatal(err)
|
|
+ }
|
|
+ if err := (&ErrorQueryHook{Logger: logger}).AfterProcess(h); err != nil {
|
|
+ t.Fatal(err)
|
|
+ }
|
|
+ found := strings.Contains(strings.Join(logger.entries, "\n"), secret)
|
|
+ if found == sensitive {
|
|
+ t.Fatal("query hook disclosure/redaction mismatch")
|
|
+ }
|
|
+ var out bytes.Buffer
|
|
+ if err := trace.Start(&out); err != nil {
|
|
+ t.Fatal(err)
|
|
+ }
|
|
+ next, err := (TracingHook{}).BeforeProcess(h)
|
|
+ if err != nil {
|
|
+ trace.Stop()
|
|
+ t.Fatal(err)
|
|
+ }
|
|
+ h.Ctx = next
|
|
+ err = (TracingHook{}).AfterProcess(h)
|
|
+ trace.Stop()
|
|
+ if err != nil {
|
|
+ t.Fatal(err)
|
|
+ }
|
|
+ if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive {
|
|
+ t.Fatal("trace disclosure/redaction mismatch")
|
|
+ }
|
|
+ }
|
|
+}
|