Skip to content

Commit 1c41e8a

Browse files
authored
Refactor duplicated DIFC pipeline decisions and logger level wrappers (#4301)
This PR addresses the duplicate-code report by extracting repeated DIFC enforcement decisions shared between unified MCP tool calls and proxy REST handling, and by collapsing repeated logger wrapper patterns. The goal is to reduce drift risk in policy behavior and simplify maintenance of level-based logging APIs. - **DIFC pipeline decision logic centralized** - Added `internal/difc/pipeline_decisions.go` with shared predicates for: - coarse-deny bypass on reads - `LabelResponse` eligibility by operation/mode - strict-mode blocking on filtered collections - propagate-mode label accumulation rules - Replaced inline condition blocks in: - `internal/server/unified.go` - `internal/proxy/handler.go` - **Logger 4-wrapper pattern consolidated** - Added generic wrapper constructors in `internal/logger/common.go`: - `makeLevelLogger(...)` - `makeServerLevelLogger(...)` - Replaced repetitive per-level one-liner wrappers with generated wrappers while preserving public API names/signatures in: - `internal/logger/file_logger.go` - `internal/logger/markdown_logger.go` - `internal/logger/server_file_logger.go` - **Focused DIFC helper coverage** - Added `internal/difc/pipeline_decisions_test.go` to lock behavior of shared predicates across operation/mode combinations. ```go // internal/difc/pipeline_decisions.go func ShouldCallLabelResponse(operation OperationType, enforcementMode EnforcementMode) bool { isPureWrite := operation == OperationWrite return !isPureWrite && (operation != OperationReadWrite || enforcementMode != EnforcementStrict) } ``` > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `example.com` > - Triggering command: `/tmp/go-build3351015982/b509/launcher.test /tmp/go-build3351015982/b509/launcher.test -test.testlogfile=/tmp/go-build3351015982/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a -I x_amd64/vet --gdwarf-5 pickfirst/intern-atomic -o x_amd64/vet -E 3Uglm7fcz 9224174/b287/ x_amd64/vet -I . -imultiarch x_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build1206797649/b513/launcher.test /tmp/go-build1206797649/b513/launcher.test -test.testlogfile=/tmp/go-build1206797649/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s` (dns block) > - `invalid-host-that-does-not-exist-12345.com` > - Triggering command: `/tmp/go-build3351015982/b491/config.test /tmp/go-build3351015982/b491/config.test -test.testlogfile=/tmp/go-build3351015982/b491/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a -I x_amd64/vet --gdwarf-5 ternal/engine/wa-atomic -o x_amd64/vet -I piOAqIlI1 -I x_amd64/vet --gdwarf-5 re/xxhash/v2 -o x_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build1206797649/b495/config.test /tmp/go-build1206797649/b495/config.test -test.testlogfile=/tmp/go-build1206797649/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s go1.25.9 -c=4 -nolocalimports -importcfg /tmp/go-build2876961135/b477/importcfg -pack /home/REDACTED/work/gh-aw-mcpg/gh-aw-mcpg/internal/auth/header.go /home/REDACTED/work/gh-aw-mcpg/gh-aw-mcpg/internal/auth/header_test.go` (dns block) > - `nonexistent.local` > - Triggering command: `/tmp/go-build3351015982/b509/launcher.test /tmp/go-build3351015982/b509/launcher.test -test.testlogfile=/tmp/go-build3351015982/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a -I x_amd64/vet --gdwarf-5 pickfirst/intern-atomic -o x_amd64/vet -E 3Uglm7fcz 9224174/b287/ x_amd64/vet -I . -imultiarch x_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build1206797649/b513/launcher.test /tmp/go-build1206797649/b513/launcher.test -test.testlogfile=/tmp/go-build1206797649/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s` (dns block) > - `slow.example.com` > - Triggering command: `/tmp/go-build3351015982/b509/launcher.test /tmp/go-build3351015982/b509/launcher.test -test.testlogfile=/tmp/go-build3351015982/b509/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true g_.a -I x_amd64/vet --gdwarf-5 pickfirst/intern-atomic -o x_amd64/vet -E 3Uglm7fcz 9224174/b287/ x_amd64/vet -I . -imultiarch x_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build1206797649/b513/launcher.test /tmp/go-build1206797649/b513/launcher.test -test.testlogfile=/tmp/go-build1206797649/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s` (dns block) > - `this-host-does-not-exist-12345.com` > - Triggering command: `/tmp/go-build3351015982/b518/mcp.test /tmp/go-build3351015982/b518/mcp.test -test.testlogfile=/tmp/go-build3351015982/b518/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true .cfg elemetry.io/otel-ifaceassert x_amd64/vet . --gdwarf2 --64 x_amd64/vet -qui�� .cfg om/grpc-ecosystem/grpc-gateway/v2@v2.28.0/runtime/convert.go x_amd64/vet /tmp/go-build370bash g/grpc/internal//usr/bin/runc x86_64-linux-gnu--version x_amd64/vet` (dns block) > - Triggering command: `/tmp/go-build1206797649/b522/mcp.test /tmp/go-build1206797649/b522/mcp.test -test.testlogfile=/tmp/go-build1206797649/b522/testlog.txt -test.paniconexit0 -test.timeout=10m0s -uns�� tion_pool.go _monitor.go x_amd64/compile` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/github/gh-aw-mcpg/settings/copilot/coding_agent) (admins only) > > </details>
2 parents 25821a0 + 805548c commit 1c41e8a

8 files changed

Lines changed: 150 additions & 42 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package difc
2+
3+
// ShouldBypassCoarseDeny returns true when a coarse-grained deny should still
4+
// proceed to backend execution so Phase 5 can enforce per-item policy.
5+
func ShouldBypassCoarseDeny(operation OperationType) bool {
6+
return operation == OperationRead
7+
}
8+
9+
// ShouldCallLabelResponse returns true when guards should label response data
10+
// for possible fine-grained filtering.
11+
func ShouldCallLabelResponse(operation OperationType, enforcementMode EnforcementMode) bool {
12+
isPureWrite := operation == OperationWrite
13+
return !isPureWrite && (operation != OperationReadWrite || enforcementMode != EnforcementStrict)
14+
}
15+
16+
// ShouldBlockFilteredResponse returns true when filtered items should block the
17+
// whole response instead of returning a partially filtered result.
18+
func ShouldBlockFilteredResponse(enforcementMode EnforcementMode, filteredCount int) bool {
19+
return enforcementMode == EnforcementStrict && filteredCount > 0
20+
}
21+
22+
// ShouldAccumulateReadLabels returns true when read labels should be
23+
// accumulated back into the agent label set.
24+
func ShouldAccumulateReadLabels(operation OperationType, enforcementMode EnforcementMode) bool {
25+
return operation != OperationWrite && enforcementMode == EnforcementPropagate
26+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package difc
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestShouldBypassCoarseDeny(t *testing.T) {
10+
assert.True(t, ShouldBypassCoarseDeny(OperationRead))
11+
assert.False(t, ShouldBypassCoarseDeny(OperationWrite))
12+
assert.False(t, ShouldBypassCoarseDeny(OperationReadWrite))
13+
}
14+
15+
func TestShouldCallLabelResponse(t *testing.T) {
16+
assert.False(t, ShouldCallLabelResponse(OperationWrite, EnforcementStrict))
17+
assert.False(t, ShouldCallLabelResponse(OperationReadWrite, EnforcementStrict))
18+
assert.True(t, ShouldCallLabelResponse(OperationRead, EnforcementStrict))
19+
assert.True(t, ShouldCallLabelResponse(OperationReadWrite, EnforcementFilter))
20+
assert.True(t, ShouldCallLabelResponse(OperationReadWrite, EnforcementPropagate))
21+
}
22+
23+
func TestShouldBlockFilteredResponse(t *testing.T) {
24+
assert.True(t, ShouldBlockFilteredResponse(EnforcementStrict, 1))
25+
assert.False(t, ShouldBlockFilteredResponse(EnforcementStrict, 0))
26+
assert.False(t, ShouldBlockFilteredResponse(EnforcementFilter, 3))
27+
assert.False(t, ShouldBlockFilteredResponse(EnforcementPropagate, 2))
28+
}
29+
30+
func TestShouldAccumulateReadLabels(t *testing.T) {
31+
assert.True(t, ShouldAccumulateReadLabels(OperationRead, EnforcementPropagate))
32+
assert.True(t, ShouldAccumulateReadLabels(OperationReadWrite, EnforcementPropagate))
33+
assert.False(t, ShouldAccumulateReadLabels(OperationWrite, EnforcementPropagate))
34+
assert.False(t, ShouldAccumulateReadLabels(OperationRead, EnforcementStrict))
35+
assert.False(t, ShouldAccumulateReadLabels(OperationRead, EnforcementFilter))
36+
}

internal/logger/common.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,38 +183,64 @@ import (
183183

184184
// Log-Level Quad-Function Pattern
185185
//
186-
// Three sets of four public functions — one set per logger variant — share an identical
187-
// structural pattern where each function is a one-liner that delegates to an internal
188-
// helper with the appropriate LogLevel constant:
186+
// Three sets of four public functions — one set per logger variant — share an
187+
// identical structure where each exported one-liner delegates to an unexported
188+
// per-level closure created by helper constructors in this file:
189189
//
190190
// func Log<Level>(category, format string, args ...interface{}) {
191-
// <internalHelper>(LogLevel<Level>, category, format, args...)
191+
// log<level>(category, format, args...)
192192
// }
193193
//
194-
// The three sets and their internal helpers are:
194+
// The three sets and their internal dispatch helpers are:
195195
//
196196
// file_logger.go LogInfo / LogWarn / LogError / LogDebug → logWithLevel
197197
// markdown_logger.go LogInfoMd / LogWarnMd / LogErrorMd / LogDebugMd → logWithMarkdown
198198
// server_file_logger.go LogInfoWithServer / ... / LogDebugWithServer → logWithLevelAndServer
199199
//
200-
// This pattern is intentionally kept across the three files because:
200+
// This pattern keeps exported APIs immutable (`func` declarations) while still
201+
// eliminating repeated inline level wiring.
202+
//
203+
// The makeLevelLogger and makeServerLevelLogger helpers are for internal
204+
// delegation only and should not replace exported functions with reassignable
205+
// function variables.
206+
//
207+
// This remains intentionally consistent across the three files because:
201208
// - Each set is a distinct public API with a different signature and set of callers.
202-
// - The one-liner wrappers are trivial and unlikely to diverge.
203-
// - Go lacks the metaprogramming to eliminate them without sacrificing readability.
209+
// - The exported wrappers preserve a stable, non-mutable API surface.
210+
// - Internal closure generation removes repetitive level-binding boilerplate.
204211
//
205212
// The shared logFuncs map below centralises the LogLevel → log-function
206213
// mapping so that the internal helpers (logWithMarkdown, logWithLevelAndServer)
207214
// do not need their own switch-on-level blocks.
208215
//
209216
// When adding a new LogLevel constant (e.g., LogLevelTrace):
210217
// 1. Add a new entry to the logFuncs map below.
211-
// 2. Add a new LogTrace wrapper to each of the three files above.
218+
// 2. Add a new internal per-level closure and exported wrapper in each of the
219+
// three files above.
212220
//
213221
// logFuncs maps each LogLevel to its corresponding global log function.
214222
// This eliminates repeated switch-on-level blocks in logWithMarkdown
215223
// (markdown_logger.go) and logWithLevelAndServer (server_file_logger.go).
216224
// When adding a new LogLevel constant, add a corresponding entry here so
217225
// that all dispatch sites automatically support the new level.
226+
func makeLevelLogger(
227+
dispatch func(level LogLevel, category, format string, args ...interface{}),
228+
level LogLevel,
229+
) func(category, format string, args ...interface{}) {
230+
return func(category, format string, args ...interface{}) {
231+
dispatch(level, category, format, args...)
232+
}
233+
}
234+
235+
func makeServerLevelLogger(
236+
dispatch func(serverID string, level LogLevel, category, format string, args ...interface{}),
237+
level LogLevel,
238+
) func(serverID, category, format string, args ...interface{}) {
239+
return func(serverID, category, format string, args ...interface{}) {
240+
dispatch(serverID, level, category, format, args...)
241+
}
242+
}
243+
218244
var logFuncs = map[LogLevel]func(string, string, ...interface{}){
219245
LogLevelInfo: LogInfo,
220246
LogLevelWarn: LogWarn,

internal/logger/file_logger.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,24 +113,31 @@ func logWithLevel(level LogLevel, category, format string, args ...interface{})
113113
})
114114
}
115115

116-
// LogInfo logs an informational message
116+
var (
117+
logInfo = makeLevelLogger(logWithLevel, LogLevelInfo)
118+
logWarn = makeLevelLogger(logWithLevel, LogLevelWarn)
119+
logError = makeLevelLogger(logWithLevel, LogLevelError)
120+
logDebug = makeLevelLogger(logWithLevel, LogLevelDebug)
121+
)
122+
123+
// LogInfo logs an informational message.
117124
func LogInfo(category, format string, args ...interface{}) {
118-
logWithLevel(LogLevelInfo, category, format, args...)
125+
logInfo(category, format, args...)
119126
}
120127

121-
// LogWarn logs a warning message
128+
// LogWarn logs a warning message.
122129
func LogWarn(category, format string, args ...interface{}) {
123-
logWithLevel(LogLevelWarn, category, format, args...)
130+
logWarn(category, format, args...)
124131
}
125132

126-
// LogError logs an error message
133+
// LogError logs an error message.
127134
func LogError(category, format string, args ...interface{}) {
128-
logWithLevel(LogLevelError, category, format, args...)
135+
logError(category, format, args...)
129136
}
130137

131-
// LogDebug logs a debug message
138+
// LogDebug logs a debug message.
132139
func LogDebug(category, format string, args ...interface{}) {
133-
logWithLevel(LogLevelDebug, category, format, args...)
140+
logDebug(category, format, args...)
134141
}
135142

136143
// CloseGlobalLogger closes the global file logger

internal/logger/markdown_logger.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,24 +180,31 @@ func logWithMarkdown(level LogLevel, category, format string, args ...interface{
180180
})
181181
}
182182

183-
// LogInfoMd logs to both regular and markdown loggers
183+
var (
184+
logInfoMd = makeLevelLogger(logWithMarkdown, LogLevelInfo)
185+
logWarnMd = makeLevelLogger(logWithMarkdown, LogLevelWarn)
186+
logErrorMd = makeLevelLogger(logWithMarkdown, LogLevelError)
187+
logDebugMd = makeLevelLogger(logWithMarkdown, LogLevelDebug)
188+
)
189+
190+
// LogInfoMd logs to both regular and markdown loggers.
184191
func LogInfoMd(category, format string, args ...interface{}) {
185-
logWithMarkdown(LogLevelInfo, category, format, args...)
192+
logInfoMd(category, format, args...)
186193
}
187194

188-
// LogWarnMd logs to both regular and markdown loggers
195+
// LogWarnMd logs to both regular and markdown loggers.
189196
func LogWarnMd(category, format string, args ...interface{}) {
190-
logWithMarkdown(LogLevelWarn, category, format, args...)
197+
logWarnMd(category, format, args...)
191198
}
192199

193-
// LogErrorMd logs to both regular and markdown loggers
200+
// LogErrorMd logs to both regular and markdown loggers.
194201
func LogErrorMd(category, format string, args ...interface{}) {
195-
logWithMarkdown(LogLevelError, category, format, args...)
202+
logErrorMd(category, format, args...)
196203
}
197204

198-
// LogDebugMd logs to both regular and markdown loggers
205+
// LogDebugMd logs to both regular and markdown loggers.
199206
func LogDebugMd(category, format string, args ...interface{}) {
200-
logWithMarkdown(LogLevelDebug, category, format, args...)
207+
logDebugMd(category, format, args...)
201208
}
202209

203210
// CloseMarkdownLogger closes the global markdown logger

internal/logger/server_file_logger.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,24 +155,31 @@ func logWithLevelAndServer(serverID string, level LogLevel, category, format str
155155
}
156156
}
157157

158-
// LogInfoWithServer logs an informational message to the server-specific log file
158+
var (
159+
logInfoWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelInfo)
160+
logWarnWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelWarn)
161+
logErrorWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelError)
162+
logDebugWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelDebug)
163+
)
164+
165+
// LogInfoWithServer logs an informational message to the server-specific log file.
159166
func LogInfoWithServer(serverID, category, format string, args ...interface{}) {
160-
logWithLevelAndServer(serverID, LogLevelInfo, category, format, args...)
167+
logInfoWithServer(serverID, category, format, args...)
161168
}
162169

163-
// LogWarnWithServer logs a warning message to the server-specific log file
170+
// LogWarnWithServer logs a warning message to the server-specific log file.
164171
func LogWarnWithServer(serverID, category, format string, args ...interface{}) {
165-
logWithLevelAndServer(serverID, LogLevelWarn, category, format, args...)
172+
logWarnWithServer(serverID, category, format, args...)
166173
}
167174

168-
// LogErrorWithServer logs an error message to the server-specific log file
175+
// LogErrorWithServer logs an error message to the server-specific log file.
169176
func LogErrorWithServer(serverID, category, format string, args ...interface{}) {
170-
logWithLevelAndServer(serverID, LogLevelError, category, format, args...)
177+
logErrorWithServer(serverID, category, format, args...)
171178
}
172179

173-
// LogDebugWithServer logs a debug message to the server-specific log file
180+
// LogDebugWithServer logs a debug message to the server-specific log file.
174181
func LogDebugWithServer(serverID, category, format string, args ...interface{}) {
175-
logWithLevelAndServer(serverID, LogLevelDebug, category, format, args...)
182+
logDebugWithServer(serverID, category, format, args...)
176183
}
177184

178185
// CloseServerFileLogger closes the global server file logger

internal/proxy/handler.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func (h *proxyHandler) handleWithDIFC(w http.ResponseWriter, r *http.Request, pa
178178
evalResult := s.evaluator.Evaluate(agentLabels.Secrecy, agentLabels.Integrity, resource, operation)
179179

180180
if !evalResult.IsAllowed() {
181-
if operation == difc.OperationRead {
181+
if difc.ShouldBypassCoarseDeny(operation) {
182182
// Read in filter mode: skip coarse block, proceed to fine-grained filtering
183183
logHandler.Printf("[DIFC] Phase 2: coarse check failed for read, proceeding to Phase 3")
184184
} else {
@@ -266,7 +266,7 @@ func (h *proxyHandler) handleWithDIFC(w http.ResponseWriter, r *http.Request, pa
266266
}
267267

268268
// Strict mode: block entire response if any item filtered
269-
if s.enforcementMode == difc.EnforcementStrict && filtered.GetFilteredCount() > 0 {
269+
if difc.ShouldBlockFilteredResponse(s.enforcementMode, filtered.GetFilteredCount()) {
270270
logHandler.Printf("[DIFC] STRICT: blocking response — %d filtered items", filtered.GetFilteredCount())
271271
writeDIFCForbidden(w, fmt.Sprintf("DIFC policy violation: %d of %d items not accessible",
272272
filtered.GetFilteredCount(), filtered.TotalCount))
@@ -318,7 +318,7 @@ func (h *proxyHandler) handleWithDIFC(w http.ResponseWriter, r *http.Request, pa
318318
}
319319

320320
// **Phase 6: Label accumulation (propagate mode)**
321-
if s.enforcementMode == difc.EnforcementPropagate && labeledData != nil {
321+
if labeledData != nil && difc.ShouldAccumulateReadLabels(operation, s.enforcementMode) {
322322
overall := labeledData.Overall()
323323
agentLabels.AccumulateFromRead(overall)
324324
logHandler.Printf("[DIFC] Phase 6: accumulated labels")

internal/server/unified.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ func (us *UnifiedServer) callBackendTool(ctx context.Context, serverID, toolName
531531
// For read operations in any mode, we skip the coarse-grained block
532532
// and let the request proceed. Fine-grained filtering at Phase 5 will filter
533533
// individual items from the response based on their actual labels from LabelResponse().
534-
isReadOperation := (operation == difc.OperationRead)
534+
isReadOperation := difc.ShouldBypassCoarseDeny(operation)
535535
result := requestEvaluator.Evaluate(agentLabels.Secrecy, agentLabels.Integrity, resource, operation)
536536

537537
if !result.IsAllowed() {
@@ -603,8 +603,7 @@ func (us *UnifiedServer) callBackendTool(ctx context.Context, serverID, toolName
603603
// Per spec: LabelResponse() is only called for read operations in all modes,
604604
// and for read-write operations in filter/propagate modes.
605605
// For write operations and read-write in strict mode, skip LabelResponse().
606-
isPureWrite := (operation == difc.OperationWrite)
607-
shouldCallLabelResponse := !isPureWrite && (operation != difc.OperationReadWrite || enforcementMode != difc.EnforcementStrict)
606+
shouldCallLabelResponse := difc.ShouldCallLabelResponse(operation, enforcementMode)
608607

609608
var labeledData difc.LabeledData
610609
if shouldCallLabelResponse {
@@ -631,7 +630,7 @@ func (us *UnifiedServer) callBackendTool(ctx context.Context, serverID, toolName
631630
filtered.GetAccessibleCount(), filtered.TotalCount)
632631

633632
// **Strict mode: block entire response if ANY item is filtered**
634-
if enforcementMode == difc.EnforcementStrict && filtered.GetFilteredCount() > 0 {
633+
if difc.ShouldBlockFilteredResponse(enforcementMode, filtered.GetFilteredCount()) {
635634
logger.LogWarn("difc", "STRICT MODE: Blocking entire response - %d/%d items violate DIFC policy",
636635
filtered.GetFilteredCount(), filtered.TotalCount)
637636
blockErr := fmt.Errorf("DIFC policy violation: %d of %d items in response are not accessible to agent %s",
@@ -664,7 +663,7 @@ func (us *UnifiedServer) callBackendTool(ctx context.Context, serverID, toolName
664663
// **Phase 6: Accumulate labels from this operation (for reads in PROPAGATE mode only)**
665664
// Label accumulation should only happen when mode is EnforcementPropagate
666665
// Filter mode does NOT accumulate - it just filters what the agent can see
667-
if !isPureWrite && enforcementMode == difc.EnforcementPropagate {
666+
if difc.ShouldAccumulateReadLabels(operation, enforcementMode) {
668667
overall := labeledData.Overall()
669668
agentLabels.AccumulateFromRead(overall)
670669
logUnified.Printf("[DIFC] Agent %s accumulated labels (propagate mode) | Secrecy: %v | Integrity: %v",
@@ -675,7 +674,7 @@ func (us *UnifiedServer) callBackendTool(ctx context.Context, serverID, toolName
675674
finalResult = backendResult
676675

677676
// **Phase 6: Accumulate labels from resource (for reads in PROPAGATE mode only)**
678-
if !isPureWrite && enforcementMode == difc.EnforcementPropagate {
677+
if difc.ShouldAccumulateReadLabels(operation, enforcementMode) {
679678
agentLabels.AccumulateFromRead(resource)
680679
logUnified.Printf("[DIFC] Agent %s accumulated labels (propagate mode) | Secrecy: %v | Integrity: %v",
681680
agentID, agentLabels.GetSecrecyTags(), agentLabels.GetIntegrityTags())

0 commit comments

Comments
 (0)