Skip to content

Fix EI_EXPOSE_REP: Return defensive copies from collection getters in DTOs#29

Merged
brunoborges merged 3 commits intomainfrom
copilot/fix-spotbugs-ei-expose-rep
Feb 6, 2026
Merged

Fix EI_EXPOSE_REP: Return defensive copies from collection getters in DTOs#29
brunoborges merged 3 commits intomainfrom
copilot/fix-spotbugs-ei-expose-rep

Conversation

Copy link
Contributor

Copilot AI commented Feb 6, 2026

SpotBugs identified 95 instances of EI_EXPOSE_REP (CWE-374) where getters in DTO/event classes expose mutable collections, allowing external modification of internal state.

Changes

Applied defensive copy pattern to collection getters across 16 classes in events and json packages:

  • Lists: Wrap with Collections.unmodifiableList()
  • Maps: Wrap with Collections.unmodifiableMap()
  • Arrays: Return .clone()
  • Preserve null-safety with ternary checks

Example

Before:

public List<ToolDefinition> getTools() {
    return tools;
}

public Map<String, Object> getMcpServers() {
    return mcpServers;
}

After:

public List<ToolDefinition> getTools() {
    return tools == null ? null : Collections.unmodifiableList(tools);
}

public Map<String, Object> getMcpServers() {
    return mcpServers == null ? null : Collections.unmodifiableMap(mcpServers);
}

Scope

Affected classes include configuration DTOs (SessionConfig, CreateSessionRequest, ResumeSessionConfig), event data classes (AssistantMessageEvent, AssistantUsageEvent, UserMessageEvent), and related types. Setters unchanged - Jackson deserialization remains functional. No API-breaking changes.

Remaining EI_EXPOSE_REP findings relate to getData() methods returning complete data objects, not addressed in this PR.

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:

  • checkstyle.org
    • Triggering command: /opt/hostedtoolcache/CodeQL/2.24.0/x64/codeql/tools/linux64/java/bin/java /opt/hostedtoolcache/CodeQL/2.24.0/x64/codeql/tools/linux64/java/bin/java -jar /opt/hostedtoolcache/CodeQL/2.24.0/x64/codeql/xml/tools/xml-extractor.jar --fileList=/home/REDACTED/work/copilot-sdk-java/.codeql-scratch/dbs/java/working/files-to-index10602877755701355398.list --sourceArchiveDir=/home/REDACTED/work/copilot-sdk-java/.codeql-scratch/dbs/java/src --outputDir=/home/REDACTED/work/copilot-sdk-java/.codeql-scratch/dbs/java/trap/java which n-Hotspot_jdk/17.0.18-8/x64/bin/java (dns block)
  • https://api.github.com/copilot_internal/user
    • Triggering command: /home/REDACTED/work/copilot-sdk-java/copilot-sdk-java/target/copilot-sdk/test/harness/node_modules/@github/copilot-linux-x64/copilot /home/REDACTED/work/copilot-sdk-java/copilot-sdk-java/target/copilot-sdk/test/harness/node_modules/@github/copilot-linux-x64/copilot --server --log-level info --stdio --auth-token-env COPILOT_SDK_AUTH_TOKEN --no-auto-login (http block)
    • Triggering command: /home/REDACTED/work/copilot-sdk-java/copilot-sdk-java/target/copilot-sdk/test/harness/node_modules/@github/copilot-linux-x64/copilot /home/REDACTED/work/copilot-sdk-java/copilot-sdk-java/target/copilot-sdk/test/harness/node_modules/@github/copilot-linux-x64/copilot --server --log-level info --stdio --auth-token-env COPILOT_SDK_AUTH_TOKEN --no-auto-login r/.m2/repository/usr/lib/jvm/temurin-17-jdk-amd64/lib/jspawnhelper (http block)
    • Triggering command: /home/REDACTED/work/copilot-sdk-java/copilot-sdk-java/target/copilot-sdk/test/harness/node_modules/@github/copilot-linux-x64/copilot /home/REDACTED/work/copilot-sdk-java/copilot-sdk-java/target/copilot-sdk/test/harness/node_modules/@github/copilot-linux-x64/copilot --server --log-level info --stdio --auth-token-env COPILOT_SDK_AUTH_TOKEN --no-auto-login /sdk/json/ModelSgit /sdk/json/ModelCrev-parse /sdk/json/Sessio--show-toplevel /sdk/json/GetForegroundSessionResponse.java /sdk�� /sdk/json/ModelInfo.java /sdk/json/UserInputHandler.java ache/Java_Temurin-Hotspot_jdk/17.0.18-8/x64/bin/grep 8bf1bbb6 (http block)

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

Original prompt

This section details on the original issue you should resolve

<issue_title>SpotBugs: Getters expose internal mutable objects (EI_EXPOSE_REP, 95 instances)</issue_title>
<issue_description>## SpotBugs Finding: EI_EXPOSE_REP

Category: MALICIOUS_CODE
Priority: 2 (Normal) | Rank: 18
Bug Type: EI_EXPOSE_REP
CWE: CWE-374
Instances: 95

Description

Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to return a defensive copy.

Affected Classes

These findings are concentrated in DTO/event classes in the events and json packages:

Events Package (com.github.copilot.sdk.events)

  • AbortEventgetData()
  • AssistantIntentEventgetData()
  • AssistantMessageDeltaEventgetData()
  • AssistantMessageEventgetData(), getToolRequests()
  • AssistantReasoningDeltaEventgetData()
  • AssistantReasoningEventgetData()
  • AssistantTurnEndEventgetData()
  • AssistantTurnStartEventgetData()
  • AssistantUsageEventgetData(), getQuotaSnapshots()
  • HookEndEventgetData()
  • HookStartEventgetData()
  • SessionIdleEventgetData()
  • ToolExecutionEndEventgetData()
  • ToolExecutionStartEventgetData()

JSON Package (com.github.copilot.sdk.json)

  • CreateSessionRequestgetTools(), getMcpServers(), getCustomAgents(), getSkillDirectories(), getDisabledSkills()
  • ResumeSessionConfiggetTools(), getMcpServers(), getCustomAgents(), getSkillDirectories(), getDisabledSkills()
  • ResumeSessionRequestgetTools(), getMcpServers(), getCustomAgents(), getSkillDirectories(), getDisabledSkills()
  • SendMessageRequestgetAttachments()
  • SessionConfiggetTools(), getAvailableTools(), getExcludedTools(), getMcpServers(), getCustomAgents(), getSkillDirectories(), getDisabledSkills()
  • SessionEndHookOutputgetCleanupActions()
  • SessionLifecycleEventgetMetadata()
  • SessionStartHookOutputgetModifiedConfig()
  • ToolResultObjectgetBinaryResultsForLlm(), getToolTelemetry()
  • UserInputRequestgetChoices()

Decision Needed

For DTO/POJO classes used for JSON serialization, this is generally a low-risk finding since:

  1. These objects are deserialized from JSON-RPC and consumed locally
  2. Defensive copying would add overhead without meaningful security benefit
  3. Jackson deserialization relies on mutable setters

Options:

  • Suppress: Add @SuppressFBWarnings("EI_EXPOSE_REP") to DTO packages
  • Fix with records: Convert simple DTOs to Java records (immutable by default)
  • Defensive copy: Return Collections.unmodifiableList() / Map.copyOf() from getters

Generated from SpotBugs 4.9.8 static analysis</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 6, 2026 02:49
…REP findings

Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
…ackages

Co-authored-by: brunoborges <129743+brunoborges@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix SpotBugs EI_EXPOSE_REP issues in DTO classes Fix EI_EXPOSE_REP: Return defensive copies from collection getters in DTOs Feb 6, 2026
Copilot AI requested a review from brunoborges February 6, 2026 02:54
@brunoborges brunoborges marked this pull request as ready for review February 6, 2026 03:03
@brunoborges brunoborges merged commit 922ec0e into main Feb 6, 2026
2 checks passed
@brunoborges brunoborges deleted the copilot/fix-spotbugs-ei-expose-rep branch February 16, 2026 15:42
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.

SpotBugs: Getters expose internal mutable objects (EI_EXPOSE_REP, 95 instances)

2 participants