the-custodian/docs/changes/netkingdom-target-2026-10-17/apps-operation-backend-r1/forgejo-private-query-common.patch
codex 109a90a424
Some checks are pending
CI Smoke / host-smoke (push) Waiting to run
CI Smoke / container-smoke (push) Waiting to run
Reject generated-only commits / generated-commit-guard (push) Waiting to run
docs: preserve reviewed NetKingdom session artifacts
2026-11-01 23:20:36 +01:00

188 lines
5.6 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
+}
--- a/models/db/context.go
+++ b/models/db/context.go
@@ -72,6 +72,12 @@
// GetEngine will get a db Engine from this context or return an Engine restricted to this context
func GetEngine(ctx context.Context) Engine {
if e := getEngine(ctx); e != nil {
+ // A private-query marker must reach XORM's real query hooks, including
+ // inside an existing engine/transaction context. Wrapping the Go
+ // context alone does not change XORM's stored session context.
+ if isSensitiveSQL(ctx) {
+ return e.Context(ctx)
+ }
return e
}
return x.Context(ctx)
--- /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,103 @@
+// 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"
+ "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")
+ }
+ }
+}
+
+func TestSensitiveSQLActualContextPropagation(t *testing.T) {
+ engine, ok := GetEngine(DefaultContext).(*xorm.Engine)
+ if !ok {
+ t.Fatal("missing actual fixture engine")
+ }
+ logger := new(sensitiveSQLLog)
+ engine.AddHook(&SlowQueryHook{Logger: logger})
+ engine.AddHook(&ErrorQueryHook{Logger: logger})
+ engine.AddHook(TracingHook{})
+ const secret = "synthetic-actual-xorm-private-sentinel"
+ for _, sensitive := range []bool{false, true} {
+ logger.entries = nil
+ ctx := context.Context(DefaultContext)
+ if sensitive {
+ ctx = WithSensitiveSQL(ctx)
+ }
+ var out bytes.Buffer
+ if err := trace.Start(&out); err != nil {
+ t.Fatal(err)
+ }
+ _, err := GetEngine(ctx).Exec("SELECT missing_synthetic_function(?)", secret)
+ trace.Stop()
+ if err == nil {
+ t.Fatal("expected actual SQLite function refusal")
+ }
+ if len(logger.entries) < 2 {
+ t.Fatal("actual query hooks did not execute")
+ }
+ if strings.Contains(strings.Join(logger.entries, "\n"), secret) == sensitive {
+ t.Fatal("actual XORM query context did not preserve redaction")
+ }
+ if bytes.Contains(out.Bytes(), []byte(secret)) == sensitive {
+ t.Fatal("actual XORM trace context did not preserve redaction")
+ }
+ }
+}