Skip to content

Refactor duplicated DIFC pipeline decisions and logger level wrappers#4301

Merged
lpcox merged 3 commits intomainfrom
copilot/fix-duplicate-code-analysis
Apr 22, 2026
Merged

Refactor duplicated DIFC pipeline decisions and logger level wrappers#4301
lpcox merged 3 commits intomainfrom
copilot/fix-duplicate-code-analysis

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 21, 2026

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.
// internal/difc/pipeline_decisions.go
func ShouldCallLabelResponse(operation OperationType, enforcementMode EnforcementMode) bool {
	isPureWrite := operation == OperationWrite
	return !isPureWrite && (operation != OperationReadWrite || enforcementMode != EnforcementStrict)
}

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

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:

Copilot AI linked an issue Apr 21, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Fix duplicate code issues in Go source files Refactor duplicated DIFC pipeline decisions and logger level wrappers Apr 21, 2026
Copilot AI requested a review from lpcox April 21, 2026 23:53
@lpcox lpcox marked this pull request as ready for review April 22, 2026 00:00
Copilot AI review requested due to automatic review settings April 22, 2026 00:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reduces duplication in two areas: DIFC enforcement decision predicates shared by the unified MCP tool-call path and the proxy REST handler, and the repeated per-level logger wrapper functions.

Changes:

  • Centralized DIFC “pipeline decision” predicates into internal/difc/pipeline_decisions.go and updated unified/proxy callers to use them.
  • Consolidated repeated logger level wrapper patterns via helper constructors in internal/logger/common.go and refactored file/markdown/server loggers to use them.
  • Added targeted DIFC helper tests in internal/difc/pipeline_decisions_test.go.
Show a summary per file
File Description
internal/server/unified.go Swaps inline DIFC decision conditionals for shared difc helper predicates.
internal/proxy/handler.go Uses shared difc helper predicates for coarse-deny bypass, strict blocking, and propagate accumulation.
internal/difc/pipeline_decisions.go Introduces shared DIFC decision helpers to avoid drift across enforcement call sites.
internal/difc/pipeline_decisions_test.go Adds unit tests locking helper behavior across operation/mode combinations.
internal/logger/common.go Adds helper constructors for per-level wrapper creation (and retains logFuncs dispatch map).
internal/logger/file_logger.go Refactors LogInfo/LogWarn/LogError/LogDebug wrappers to use helper constructor.
internal/logger/markdown_logger.go Refactors markdown wrapper set (Log*Md) to use helper constructor.
internal/logger/server_file_logger.go Refactors per-server wrapper set (Log*WithServer) to use helper constructor.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

internal/proxy/handler.go:186

  • The comment in this branch says “Read in filter mode” / “Write blocked”, but the predicate is now ShouldBypassCoarseDeny(operation) (i.e., read operations regardless of enforcement mode), and the else branch covers both write and read-write. Please update the comments to match the actual condition/behavior so future diffs don’t reintroduce drift.
		if difc.ShouldBypassCoarseDeny(operation) {
			// Read in filter mode: skip coarse block, proceed to fine-grained filtering
			logHandler.Printf("[DIFC] Phase 2: coarse check failed for read, proceeding to Phase 3")
		} else {
			// Write blocked
			logHandler.Printf("[DIFC] Phase 2: BLOCKED %s %s — %s", r.Method, path, evalResult.Reason)
  • Files reviewed: 8/8 changed files
  • Comments generated: 4

Comment thread internal/logger/file_logger.go Outdated
Comment on lines 117 to 126
// LogInfo logs an informational message.
LogInfo = makeLevelLogger(logWithLevel, LogLevelInfo)
// LogWarn logs a warning message.
LogWarn = makeLevelLogger(logWithLevel, LogLevelWarn)
// LogError logs an error message.
LogError = makeLevelLogger(logWithLevel, LogLevelError)
// LogDebug logs a debug message.
LogDebug = makeLevelLogger(logWithLevel, LogLevelDebug)
)

Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These exported logging entry points are now variables holding function values. That makes them reassignable by any code in the module (package is internal, but still widely reachable), which can lead to accidental override, data races, or test pollution. Prefer keeping exported wrappers as func declarations and use an unexported closure/dispatcher helper internally (e.g., assign logInfo := makeLevelLogger(...) and have func LogInfo(...) { logInfo(...) }) so callers cannot mutate the API surface.

Suggested change
// LogInfo logs an informational message.
LogInfo = makeLevelLogger(logWithLevel, LogLevelInfo)
// LogWarn logs a warning message.
LogWarn = makeLevelLogger(logWithLevel, LogLevelWarn)
// LogError logs an error message.
LogError = makeLevelLogger(logWithLevel, LogLevelError)
// LogDebug logs a debug message.
LogDebug = makeLevelLogger(logWithLevel, LogLevelDebug)
)
logInfo = makeLevelLogger(logWithLevel, LogLevelInfo)
logWarn = makeLevelLogger(logWithLevel, LogLevelWarn)
logError = makeLevelLogger(logWithLevel, LogLevelError)
logDebug = makeLevelLogger(logWithLevel, LogLevelDebug)
)
// LogInfo logs an informational message.
func LogInfo(category, format string, args ...interface{}) {
logInfo(category, format, args...)
}
// LogWarn logs a warning message.
func LogWarn(category, format string, args ...interface{}) {
logWarn(category, format, args...)
}
// LogError logs an error message.
func LogError(category, format string, args ...interface{}) {
logError(category, format, args...)
}
// LogDebug logs a debug message.
func LogDebug(category, format string, args ...interface{}) {
logDebug(category, format, args...)
}

Copilot uses AI. Check for mistakes.
Comment thread internal/logger/markdown_logger.go Outdated
Comment on lines 184 to 193
// LogInfoMd logs to both regular and markdown loggers.
LogInfoMd = makeLevelLogger(logWithMarkdown, LogLevelInfo)
// LogWarnMd logs to both regular and markdown loggers.
LogWarnMd = makeLevelLogger(logWithMarkdown, LogLevelWarn)
// LogErrorMd logs to both regular and markdown loggers.
LogErrorMd = makeLevelLogger(logWithMarkdown, LogLevelError)
// LogDebugMd logs to both regular and markdown loggers.
LogDebugMd = makeLevelLogger(logWithMarkdown, LogLevelDebug)
)

Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These exported logging entry points are now variables holding function values, which makes them reassignable from other packages within the module. That’s a behavioral/API change compared to exported funcs and can introduce hard-to-debug overrides or data races. Consider keeping LogInfoMd/LogWarnMd/... as func wrappers and using an unexported closure from makeLevelLogger behind them.

Suggested change
// LogInfoMd logs to both regular and markdown loggers.
LogInfoMd = makeLevelLogger(logWithMarkdown, LogLevelInfo)
// LogWarnMd logs to both regular and markdown loggers.
LogWarnMd = makeLevelLogger(logWithMarkdown, LogLevelWarn)
// LogErrorMd logs to both regular and markdown loggers.
LogErrorMd = makeLevelLogger(logWithMarkdown, LogLevelError)
// LogDebugMd logs to both regular and markdown loggers.
LogDebugMd = makeLevelLogger(logWithMarkdown, LogLevelDebug)
)
logInfoMd = makeLevelLogger(logWithMarkdown, LogLevelInfo)
logWarnMd = makeLevelLogger(logWithMarkdown, LogLevelWarn)
logErrorMd = makeLevelLogger(logWithMarkdown, LogLevelError)
logDebugMd = makeLevelLogger(logWithMarkdown, LogLevelDebug)
)
// LogInfoMd logs to both regular and markdown loggers.
func LogInfoMd(category, format string, args ...interface{}) {
logInfoMd(category, format, args...)
}
// LogWarnMd logs to both regular and markdown loggers.
func LogWarnMd(category, format string, args ...interface{}) {
logWarnMd(category, format, args...)
}
// LogErrorMd logs to both regular and markdown loggers.
func LogErrorMd(category, format string, args ...interface{}) {
logErrorMd(category, format, args...)
}
// LogDebugMd logs to both regular and markdown loggers.
func LogDebugMd(category, format string, args ...interface{}) {
logDebugMd(category, format, args...)
}

Copilot uses AI. Check for mistakes.
Comment thread internal/logger/server_file_logger.go Outdated
Comment on lines 159 to 168
// LogInfoWithServer logs an informational message to the server-specific log file.
LogInfoWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelInfo)
// LogWarnWithServer logs a warning message to the server-specific log file.
LogWarnWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelWarn)
// LogErrorWithServer logs an error message to the server-specific log file.
LogErrorWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelError)
// LogDebugWithServer logs a debug message to the server-specific log file.
LogDebugWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelDebug)
)

Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching Log*WithServer from exported funcs to exported variables makes them reassignable by any internal package, which can cause accidental overrides and races. To preserve a stable, non-mutable API surface, keep these as func wrappers and have them delegate to an unexported closure created by makeServerLevelLogger.

Suggested change
// LogInfoWithServer logs an informational message to the server-specific log file.
LogInfoWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelInfo)
// LogWarnWithServer logs a warning message to the server-specific log file.
LogWarnWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelWarn)
// LogErrorWithServer logs an error message to the server-specific log file.
LogErrorWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelError)
// LogDebugWithServer logs a debug message to the server-specific log file.
LogDebugWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelDebug)
)
logInfoWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelInfo)
logWarnWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelWarn)
logErrorWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelError)
logDebugWithServer = makeServerLevelLogger(logWithLevelAndServer, LogLevelDebug)
)
// LogInfoWithServer logs an informational message to the server-specific log file.
func LogInfoWithServer(serverID, category, format string, args ...interface{}) {
logInfoWithServer(serverID, category, format, args...)
}
// LogWarnWithServer logs a warning message to the server-specific log file.
func LogWarnWithServer(serverID, category, format string, args ...interface{}) {
logWarnWithServer(serverID, category, format, args...)
}
// LogErrorWithServer logs an error message to the server-specific log file.
func LogErrorWithServer(serverID, category, format string, args ...interface{}) {
logErrorWithServer(serverID, category, format, args...)
}
// LogDebugWithServer logs a debug message to the server-specific log file.
func LogDebugWithServer(serverID, category, format string, args ...interface{}) {
logDebugWithServer(serverID, category, format, args...)
}

Copilot uses AI. Check for mistakes.
Comment thread internal/logger/common.go
Comment on lines +218 to +225
func makeLevelLogger(
dispatch func(level LogLevel, category, format string, args ...interface{}),
level LogLevel,
) func(category, format string, args ...interface{}) {
return func(category, format string, args ...interface{}) {
dispatch(level, category, format, args...)
}
}
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The large block comment immediately above this helper says the per-level one-liner wrappers are “intentionally kept” and instructs adding new wrappers in each file when adding a LogLevel. With this refactor, that guidance is now outdated/misleading. Please update or remove that section to reflect the new pattern (and, if you keep exported wrappers as funcs, document that makeLevelLogger is meant for internal delegation rather than replacing exported functions with mutable vars).

Copilot uses AI. Check for mistakes.
@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented Apr 22, 2026

@copilot address this review feedback #4301 (review)

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 22, 2026

@copilot address this review feedback #4301 (review)

Addressed in 805548c. I updated the logger refactor to keep exported APIs as func wrappers (non-reassignable), moved level-bound closures to unexported vars, and refreshed the outdated common.go comment block to match this pattern.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build2467473078/b513/launcher.test /tmp/go-build2467473078/b513/launcher.test -test.testlogfile=/tmp/go-build2467473078/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -W ZaqPWDELC /tmp/go-build128782306/b184/ x_amd64/vet . contextprotocol/-o --64 x_amd64/vet -I olang.org/protob-s olang.org/protob-w x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet (dns block)
  • invalid-host-that-does-not-exist-12345.com
    • Triggering command: /tmp/go-build2467473078/b495/config.test /tmp/go-build2467473078/b495/config.test -test.testlogfile=/tmp/go-build2467473078/b495/testlog.txt -test.paniconexit0 -test.timeout=10m0s rtcf�� uf@v1.36.11/internal/protolazy/b-errorsas uf@v1.36.11/internal/protolazy/l-ifaceassert x_amd64/compile -I /opt/hostedtoolc/tmp/go-build1870605770/b305/vet.cfg -I x_amd64/compile go_.�� g_.a om/tetratelabs/wazero@v1.11.0/internal/engine/in-ifaceassert 64/pkg/tool/linux_amd64/vet . nal/encoding/def/tmp/go-build1870605770/b455/vet.cfg --64 64/pkg/tool/linux_amd64/vet (dns block)
  • nonexistent.local
    • Triggering command: /tmp/go-build2467473078/b513/launcher.test /tmp/go-build2467473078/b513/launcher.test -test.testlogfile=/tmp/go-build2467473078/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -W ZaqPWDELC /tmp/go-build128782306/b184/ x_amd64/vet . contextprotocol/-o --64 x_amd64/vet -I olang.org/protob-s olang.org/protob-w x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet (dns block)
  • slow.example.com
    • Triggering command: /tmp/go-build2467473078/b513/launcher.test /tmp/go-build2467473078/b513/launcher.test -test.testlogfile=/tmp/go-build2467473078/b513/testlog.txt -test.paniconexit0 -test.timeout=10m0s -W ZaqPWDELC /tmp/go-build128782306/b184/ x_amd64/vet . contextprotocol/-o --64 x_amd64/vet -I olang.org/protob-s olang.org/protob-w x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet (dns block)
  • this-host-does-not-exist-12345.com
    • Triggering command: /tmp/go-build2467473078/b522/mcp.test /tmp/go-build2467473078/b522/mcp.test -test.testlogfile=/tmp/go-build2467473078/b522/testlog.txt -test.paniconexit0 -test.timeout=10m0s 7823�� wkO_kTNLT olang.org/grpc@v1.80.0/credentials/tls.go x_amd64/vet --gdwarf-5 --64 -o x_amd64/vet 0240�� olang.org/grpc@v1.80.0/balancer/roundrobin/roundrobin.go -buildtags x_amd64/vet -errorsas g/grpc/metadata -nilfunc x_amd64/vet (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code Analysis Report

3 participants