Skip to content

[duplicate-code] Duplicate Code Pattern: Guard Interface Structural Duplication (LabelAgent / LabelResponse) #2363

@github-actions

Description

@github-actions

Part of duplicate code analysis: #2361

Summary

internal/guard/noop.go and internal/guard/write_sink.go both implement the Guard interface and contain near-identical structural duplication in LabelAgent (returns empty AgentLabelsPayload) and LabelResponse (returns nil, nil). The LabelAgent bodies differ only by the value of the DIFCMode field.

Duplication Details

Pattern: Near-Identical LabelAgent Method Body

  • Severity: Medium

  • Occurrences: 2 implementations

  • Locations:

    • internal/guard/noop.go lines 28–37
    • internal/guard/write_sink.go lines 71–79
  • Code Sample:

    // noop.go (lines 28–37)
    func (g *NoopGuard) LabelAgent(ctx context.Context, policy interface{}, backend BackendCaller, caps *difc.Capabilities) (*LabelAgentResult, error) {
        log.Printf("Initializing agent labels with noop guard")
        return &LabelAgentResult{
            Agent: AgentLabelsPayload{
                Secrecy:   []string{},
                Integrity: []string{},
            },
            DIFCMode: difc.ModeStrict,  // ← only difference
        }, nil
    }
    
    // write_sink.go (lines 71–79)
    func (g *WriteSinkGuard) LabelAgent(_ context.Context, _ interface{}, _ BackendCaller, _ *difc.Capabilities) (*LabelAgentResult, error) {
        logWriteSink.Print("LabelAgent: returning empty labels (write-sink does not label agents)")
        return &LabelAgentResult{
            Agent: AgentLabelsPayload{
                Secrecy:   []string{},
                Integrity: []string{},
            },
            DIFCMode: difc.ModeFilter,  // ← only difference
        }, nil
    }

Pattern: Identical LabelResponse Method Body

  • Severity: Low–Medium

  • Occurrences: 2 implementations

  • Locations:

    • internal/guard/noop.go lines 60–65
    • internal/guard/write_sink.go lines 107–108
  • Code Sample:

    // Both implementations are identical:
    func (g *(Guard)) LabelResponse(...) (difc.LabeledData, error) {
        // optional log line
        return nil, nil
    }

Impact Analysis

  • Maintainability: Any change to AgentLabelsPayload structure requires updating multiple places
  • Bug Risk: Inconsistent changes (e.g., fixing initialization in one guard but not another)
  • Code Bloat: ~12 lines duplicated across guard implementations

Refactoring Recommendations

  1. Extract emptyAgentLabelsResult(mode string) *LabelAgentResult helper in internal/guard/guard.go:

    // emptyAgentLabelsResult returns a LabelAgentResult with empty agent labels for the given DIFC mode.
    func emptyAgentLabelsResult(mode string) *LabelAgentResult {
        return &LabelAgentResult{
            Agent: AgentLabelsPayload{
                Secrecy:   []string{},
                Integrity: []string{},
            },
            DIFCMode: mode,
        }
    }
    • Then noop.go calls: return emptyAgentLabelsResult(difc.ModeStrict), nil
    • And write_sink.go calls: return emptyAgentLabelsResult(difc.ModeFilter), nil
    • Estimated effort: 30 minutes
    • Benefits: Single place to update AgentLabelsPayload initialization
  2. Consider a BaseGuard embedded struct if more guard implementations are added in the future, providing default LabelResponse returning nil, nil.

Implementation Checklist

  • Add emptyAgentLabelsResult helper to internal/guard/guard.go
  • Refactor noop.go LabelAgent to use helper
  • Refactor write_sink.go LabelAgent to use helper
  • Run make test to verify no regressions

Parent Issue

See parent analysis report: #2361
Related to #2361

Generated by Duplicate Code Detector ·

Warning

⚠️ Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

  • expires on Mar 30, 2026, 3:05 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions