Skip to content

🛡️ Sentinel: [HIGH] Fix path traversal in CustomEmployeeManager#185

Open
mleem97 wants to merge 1 commit into
mainfrom
sentinel-fix-employee-path-traversal-14766504909649096741
Open

🛡️ Sentinel: [HIGH] Fix path traversal in CustomEmployeeManager#185
mleem97 wants to merge 1 commit into
mainfrom
sentinel-fix-employee-path-traversal-14766504909649096741

Conversation

@mleem97

@mleem97 mleem97 commented Jul 17, 2026

Copy link
Copy Markdown
Owner

🚨 Severity: HIGH
💡 Vulnerability: The SetPortrait method in CustomEmployeeManager uses the employeeId parameter directly to construct file paths for reading image files. If an employeeId contains traversal sequences like ../../, this can lead to reading arbitrary .jpg or .png files from the filesystem.
🎯 Impact: An attacker could potentially extract sensitive files from the server or user's filesystem by crafting malicious mod data with directory traversal in the employeeId.
🔧 Fix: Added a validation check to SetPortrait using IndexOfAny(Path.GetInvalidFileNameChars()) and Contains("..") to ensure employeeId cannot contain path separators or traversal characters.
✅ Verification: Tested via unit tests and verified via source code review.


PR created automatically by Jules for task 14766504909649096741 started by @mleem97

@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

The PR addresses a high-severity path traversal vulnerability in SetPortrait, but several critical issues must be resolved before merging. A logic error in the Fire method dispatches a 'Hired' event instead of 'Fired', which will cause significant state management issues in the application. Additionally, the sanitization logic is missing from the Register entry point, leaving the system vulnerable to save file corruption and UI breakage.

While Codacy reports the PR is 'up to standards', the manual review has identified two distinct memory leaks: one involving unmanaged Unity assets (Textures/Sprites) and another involving an ever-growing static list of delegates. Finally, although the PR description claims the fix was verified via unit tests, no such tests are present in the provided file list. These tests are required to ensure the security fix works as intended.

About this PR

  • Acceptance criteria gap: While the traversal risk is mitigated in SetPortrait, the Register method still accepts un-sanitized identifiers. This can lead to save file corruption (via newlines) or UI logic errors if not addressed at the source.
  • The PR description states that the fix was 'Tested via unit tests', but no test files are included in this PR. Please include the test suite used to verify the path traversal fix.

Test suggestions

  • Call SetPortrait with an employeeId containing directory traversal sequences (e.g., '../../etc/passwd') and verify it is blocked.
  • Call SetPortrait with an employeeId containing platform-specific invalid filename characters and verify it is blocked.
  • Call SetPortrait with a valid employeeId and verify the portrait image is successfully loaded from the ModAssets directory.
  • Verify that FireCustomEmployeeFired is dispatched correctly when calling Fire (Logic check).
  • Verify that repeatedly calling SetPortrait does not result in an unbound increase in memory usage (Leak check).
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Call `SetPortrait` with an `employeeId` containing directory traversal sequences (e.g., '../../etc/passwd') and verify it is blocked.
2. Call `SetPortrait` with an `employeeId` containing platform-specific invalid filename characters and verify it is blocked.
3. Call `SetPortrait` with a valid `employeeId` and verify the portrait image is successfully loaded from the `ModAssets` directory.
4. Verify that `FireCustomEmployeeFired` is dispatched correctly when calling `Fire` (Logic check).
5. Verify that repeatedly calling `SetPortrait` does not result in an unbound increase in memory usage (Leak check).

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

public static IReadOnlyList<CustomEmployeeEntry> Employees => _employees;
public static bool HasPendingAction => _pendingEmployeeId != null;

public static int Register(string id, string name, string description, float salary, float reputation, bool requiresConfirmation = false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 HIGH RISK

The id parameter should be validated at the entry point to prevent save file corruption (via newlines), UI breakage (via directory separators in Transform.Find), and path traversal.

Update the Register method to validate that the id string does not contain any characters from Path.GetInvalidFileNameChars(), directory separators, or newline characters. Return 0 if validation fails.

CrashLog.Log($"CustomEmployee.Fire: step 3 - dispatching CustomEmployeeFired event for '{id}'");
try
{
EventDispatcher.FireCustomEmployeeHired(id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 HIGH RISK

Critical logic error: The Fire method dispatches the FireCustomEmployeeHired event instead of a firing-specific event. This is likely a copy-paste error from the Hire method.

Suggested change
EventDispatcher.FireCustomEmployeeHired(id);
EventDispatcher.FireCustomEmployeeFired(id);

try
{
byte[] imageData = File.ReadAllBytes(imagePath);
var tex = new Texture2D(2, 2, TextureFormat.RGBA32, false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM RISK

Suggestion: Loading textures and creating sprites in this way causes a native memory leak. Consider caching these assets in a dictionary keyed by employeeId to reuse them across menu refreshes.

Implement a static Dictionary<string, Sprite> to cache loaded portraits. In SetPortrait, check the cache before loading from disk, and ensure any existing textures/sprites are properly managed to avoid leaks.

{
private static readonly List<CustomEmployeeEntry> _employees = new();
private static readonly Dictionary<string, int> _employeeIndex = new();
private static readonly List<UnityAction> _liveCallbacks = new();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM RISK

Nitpick: The _liveCallbacks list is used to store static references to UnityAction delegates but is never cleared. New actions are added every time SetupButtons is called, leading to an ever-growing memory footprint. Refactor this to clear references when the UI is closed or when cards are destroyed.

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.

1 participant