Skip to content

Override shm-size with Docker and Podman Plug-ins#1025

Open
bluna301 wants to merge 3 commits into
Project-MONAI:developfrom
bluna301:bl/shm-arg
Open

Override shm-size with Docker and Podman Plug-ins#1025
bluna301 wants to merge 3 commits into
Project-MONAI:developfrom
bluna301:bl/shm-arg

Conversation

@bluna301

@bluna301 bluna301 commented Jun 15, 2026

Copy link
Copy Markdown

Description

Allow the users to override the size of /dev/shm with the Docker and Podman plug-ins. Docker plug-in code copied from Victor's work in PR #978; code fixes adapted to Podman plug-in.

Example usage (shm_size inputs have unit of bytes):

"args": {
	"container_image": "ghcr.io/mmelqin/monai_ai_livertumor_seg_app:1.0",
	"server_url": "unix:///var/run/docker.sock",
	"entrypoint": "/bin/bash,-c",
	"command": "python3 -u /opt/monai/app/app.py",
	"task_timeout_minutes": "5",
	"shm_size": "64000000000"
},

Status

Ready

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • Breaking change (fix or new feature that would cause existing functionality to change).
  • New tests added to cover the changes.
  • All tests passed locally.
  • Documentation comments included/updated.
  • User guide updated.
  • I have updated the changelog

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Podman as a supported task manager for executing containerized workflows, providing an alternative to Docker for task execution.
    • Added configurable shared memory (/dev/shm) size support for Docker containers, enabling better control over container resource allocation.

Signed-off-by: will tepe <will.tepe@cchmc.org>
Signed-off-by: will tepe <will.tepe@cchmc.org>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@bluna301, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 47 minutes and 35 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 52655321-f7f3-4576-bb62-2f229c7a87cc

📥 Commits

Reviewing files that changed from the base of the PR and between 6f3309e and 7e3b431.

📒 Files selected for processing (9)
  • src/TaskManager/Plug-ins/Docker/DockerPlugin.cs
  • src/TaskManager/Plug-ins/Docker/Keys.cs
  • src/TaskManager/Plug-ins/Docker/Logging/Log.cs
  • src/TaskManager/Plug-ins/Podman/IPodmanContainerCreator.cs
  • src/TaskManager/Plug-ins/Podman/Keys.cs
  • src/TaskManager/Plug-ins/Podman/Logging/Log.cs
  • src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs
  • tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs
  • tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs

Walkthrough

A new Podman TaskManager plugin is introduced, mirroring the Docker plugin pattern but communicating with Podman's libpod HTTP API over a Unix domain socket. The Docker plugin is extended with shm_size support. Both plugins are registered in DI, the Podman task type is added to validation constants and plugin strings, and full unit test coverage is added for both.

Changes

Podman TaskManager Plugin + Docker shm_size

Layer / File(s) Summary
Registration, validation constants, and project wiring
src/Common/Miscellaneous/ValidationConstants.cs, src/TaskManager/TaskManager/PluginStrings.cs, src/TaskManager/TaskManager/Extensions/TaskManagerExtensions.cs, src/TaskManager/TaskManager/Monai.Deploy.WorkflowManager.TaskManager.csproj, src/Monai.Deploy.WorkflowManager.sln, src/TaskManager/TaskManager/packages.lock.json
Adds PodmanTaskType to ValidTaskTypes, adds Podman to PlugsRequiresPermanentAccounts, updates AddTaskManager DI to register Podman and Docker monitor/factory types separately, and wires project references into the solution and lock file.
Docker plugin shm_size support
src/TaskManager/Plug-ins/Docker/Keys.cs, src/TaskManager/Plug-ins/Docker/Logging/Log.cs, src/TaskManager/Plug-ins/Docker/DockerPlugin.cs, tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs
Adds Keys.ShmSize, InvalidShmSize logger, and conditional parse-and-assign in BuildContainerSpecification; two unit tests cover valid and invalid shm_size values.
Podman plugin data contracts, infrastructure, and project files
src/TaskManager/Plug-ins/Podman/ContainerVolumeMount.cs, src/TaskManager/Plug-ins/Podman/Keys.cs, src/TaskManager/Plug-ins/Podman/Strings.cs, src/TaskManager/Plug-ins/Podman/ContainerMonitorException.cs, src/TaskManager/Plug-ins/Podman/SetPermissionException.cs, src/TaskManager/Plug-ins/Podman/IPodmanClientFactory.cs, src/TaskManager/Plug-ins/Podman/IPodmanContainerCreator.cs, src/TaskManager/Plug-ins/Podman/Logging/Log.cs, src/TaskManager/Plug-ins/Podman/AssemblyInfo.cs, src/TaskManager/Plug-ins/Podman/Monai.Deploy.WorkflowManager.TaskManager.Podman.csproj
Defines volume mount and request/response DTOs, Podman and Docker status constants, custom exceptions, IPodmanClientFactory/PodmanClientFactory, IPodmanContainerCreator/PodmanContainerCreator (libpod HTTP over Unix socket), and all structured log methods.
ContainerStatusMonitor: polling, artifact upload, and callback
src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs, tests/UnitTests/TaskManager.Podman.Tests/ContainerStatusMonitorTest.cs
Defines IContainerStatusMonitor and its implementation, which polls container state, recursively uploads output artifacts to object storage, and publishes a TaskCallbackEvent; three unit tests cover no-artifact, upload-failure, and artifact-upload scenarios.
PodmanPlugin: ExecuteTask, GetStatus, HandleTimeout, and tests
src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs, tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs, tests/UnitTests/TaskManager.Podman.Tests/Monai.Deploy.WorkflowManager.TaskManager.Podman.Tests.csproj, tests/UnitTests/TaskManager.Podman.Tests/Usings.cs
Implements PodmanPlugin with constructor validation, ExecuteTask (input/output mount setup, image pull, container create/start, background monitor), GetStatus (state polling and mapping), HandleTimeout (container kill), and Dispose; comprehensive test suite with 17 test methods.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(100, 149, 237, 0.5)
    note over Caller,IContainerStatusMonitor: ExecuteTask
    Caller->>PodmanPlugin: ExecuteTask(cancellationToken)
    PodmanPlugin->>IStorageService: ListObjectsAsync / GetObjectAsync (inputs)
    PodmanPlugin->>IDockerClient: CreateImageAsync (conditional pull)
    PodmanPlugin->>IPodmanContainerCreator: CreateContainerAsync(podmanEndpoint, PodmanCreateContainerRequest)
    IPodmanContainerCreator->>PodmanLibpodAPI: POST /v4.0.0/libpod/containers/create (Unix socket)
    PodmanLibpodAPI-->>IPodmanContainerCreator: PodmanCreateContainerResponse
    PodmanPlugin->>IDockerClient: StartContainerAsync(containerId)
    PodmanPlugin->>IContainerStatusMonitor: Start(...) [background Task]
    PodmanPlugin-->>Caller: ExecutionStatus.Accepted(containerId)
  end
  rect rgba(60, 179, 113, 0.5)
    note over IContainerStatusMonitor,IMessageBrokerPublisher: Background monitoring
    loop Poll until end state or timeout
      IContainerStatusMonitor->>IDockerClient: InspectContainerAsync(containerId)
    end
    IContainerStatusMonitor->>IStorageService: PutObjectAsync(outputFiles)
    IContainerStatusMonitor->>IMessageBrokerPublisher: PublishAsync(callbackTopic, TaskCallbackEvent)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding shm-size override capability to Docker and Podman plug-ins, which is the central theme of this PR.
Description check ✅ Passed The description includes the required sections (Description, Status, Types of changes) with specific details about the feature, usage example, and appropriate checkboxes marked. However, it lacks new test assertions and missing documentation/changelog updates are noted but not completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: bluna301 <luna.bryanr@gmail.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 7

🧹 Nitpick comments (3)
tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs (1)

515-549: ⚡ Quick win

Add boundary tests for numeric-but-invalid values (0, negative).

Current tests only cover valid numeric and non-numeric strings. Please add cases asserting that shm_size = "0" and shm_size = "-1" trigger InvalidShmSize and do not set HostConfig.ShmSize.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs` around lines
515 - 549, The existing test
ExecuteTask_WhenShmSizeIsInvalid_ExpectLogWarningAndNoException in the
DockerPluginTest class only covers non-numeric string inputs for shm_size. Add
two new test methods following the same pattern to cover boundary cases: one
test that sets shm_size to "0" and another that sets it to "-1", both asserting
that they trigger an InvalidShmSize error and that HostConfig.ShmSize is not set
on the container. Use similar setup and assertion patterns as the existing test
but with these specific invalid numeric values.
tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs (1)

535-617: ⚡ Quick win

Add regression coverage for StartContainerAsync == false and numeric invalid shm_size values.

Current tests cover non-numeric shm_size, but not numeric invalid boundaries (0, negative) and not the container-start-false branch. These are important guards for execution-state correctness.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs` around lines
535 - 617, The test suite currently covers valid shm_size and non-numeric
invalid shm_size values, but is missing coverage for numeric invalid boundary
cases (zero and negative values) and the failure path when StartContainerAsync
returns false. Add two new test methods following the pattern of the existing
ExecuteTask_WhenShmSizeIsInvalid_ExpectLogWarningAndNoException test: one that
verifies behavior when shm_size is set to numeric invalid values like 0 or
negative numbers (expecting appropriate error logging), and another that
verifies the PodmanPlugin correctly handles the case when
_podmanClient.Containers.StartContainerAsync returns false instead of true,
ensuring the task result status and any error handling reflects this container
startup failure.
src/TaskManager/Plug-ins/Podman/Strings.cs (1)

32-32: ⚡ Quick win

Make terminal-state constants immutable.

Line [32] exposes DockerEndStates as a mutable List<string>. Because completion logic depends on this set, runtime mutation can cause incorrect monitor behavior.

♻️ Proposed change
-        public static readonly List<string> DockerEndStates = new() { DockerStatusExited, DockerStatusDead };
+        public static readonly IReadOnlyList<string> DockerEndStates = new[] { DockerStatusExited, DockerStatusDead };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/Strings.cs` at line 32, The DockerEndStates
field in the Strings class is exposed as a mutable List<string>, which allows
runtime modifications that could cause incorrect monitor behavior since
completion logic depends on this set. Change the type of DockerEndStates from
List<string> to IReadOnlyList<string> to make it immutable while keeping the
same initialization with DockerStatusExited and DockerStatusDead.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/TaskManager/Plug-ins/Docker/DockerPlugin.cs`:
- Around line 370-379: The code at the `long.TryParse` check in the ShmSize
handling block accepts zero and negative values without validation. Add an
additional check after successful parsing to ensure the parsed shmsize value is
greater than 0 before assigning it to parameters.HostConfig.ShmSize. If the
parsed value is not positive (less than or equal to 0), log the error using
_logger.InvalidShmSize with the invalid value and skip the assignment, ensuring
only valid positive sizes are accepted.

In `@src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs`:
- Around line 186-193: The manual substring and trim operations on relativePart
at lines 186-189 and the string concatenation forming objectName at lines
191-192 can leak platform-specific path separators like backslashes into the
object key name, breaking object storage conventions. Replace the manual path
manipulation logic with C# Path APIs (such as Path.GetRelativePath or proper
path normalization) to ensure that both relativePart and objectName consistently
use forward slashes as separators, making the code platform-agnostic and
preventing downstream retrieval issues.
- Line 78: The ArgumentNullException.ThrowIfNull call on containerTimeout is
unnecessary because TimeSpan is a value type that cannot be null. More
importantly, this misses the actual validation issue: a zero or negative
TimeSpan will cause the retry loop at line 88 to skip entirely, resulting in
silent timeouts. Remove the ArgumentNullException.ThrowIfNull call for
containerTimeout and replace it with explicit range validation that ensures
containerTimeout is a positive TimeSpan value, throwing an appropriate exception
if the value is zero or negative.

In `@src/TaskManager/Plug-ins/Podman/IPodmanClientFactory.cs`:
- Around line 28-30: The CreateClient method must validate the Podman endpoint
scheme before creating the client to prevent unintended endpoint routing. In the
CreateClient method, add validation logic to check that the podmanEndpoint
parameter uses the unix:// scheme, which is required for Podman socket
connections. If the scheme is not unix://, throw an appropriate exception with a
descriptive error message before proceeding to call CreateClient on the
DockerClientConfiguration. This ensures that only valid unix domain socket
endpoints are accepted and prevents misconfigured endpoints from being used.

In `@src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs`:
- Around line 482-486: The path construction on line 482 in the PodmanPlugin.cs
file is vulnerable to path traversal attacks because it uses simple string
replacement and trimming without validating that the resulting path stays within
the intended inputContainerDirRoot directory. An attacker could craft object
keys containing traversal sequences like "../" that would escape the container
directory and write files outside the intended task directory. To fix this,
after constructing the filePath using Path.Combine, normalize it with
Path.GetFullPath() and then validate that the normalized path starts with the
normalized inputContainerDirRoot path. If the validation fails, reject the file
path and log an error to prevent the directory creation and file write
operation.
- Around line 425-433: After successfully parsing the ShmSize value with
long.TryParse in the conditional block, add validation to reject non-positive
values (zero and negative numbers) before assigning to request.ShmSize. When the
parsed value is not greater than zero, log an appropriate error using _logger
(similar to the existing InvalidShmSize call for parse failures) and skip the
assignment. This ensures only valid positive shm_size values are accepted and
assigned to the request object.
- Around line 556-557: Replace the string concatenation approach used in the
chown Process.Start call with ProcessStartInfo that uses the ArgumentList
property to properly pass the user and path arguments separately, preventing
parsing issues if the user input contains whitespace or special characters.
Additionally, add a timeout parameter to the WaitForExit() call to prevent
indefinite blocking if the process hangs. Apply the same fix to the equivalent
chown execution in DockerPlugin.

---

Nitpick comments:
In `@src/TaskManager/Plug-ins/Podman/Strings.cs`:
- Line 32: The DockerEndStates field in the Strings class is exposed as a
mutable List<string>, which allows runtime modifications that could cause
incorrect monitor behavior since completion logic depends on this set. Change
the type of DockerEndStates from List<string> to IReadOnlyList<string> to make
it immutable while keeping the same initialization with DockerStatusExited and
DockerStatusDead.

In `@tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs`:
- Around line 515-549: The existing test
ExecuteTask_WhenShmSizeIsInvalid_ExpectLogWarningAndNoException in the
DockerPluginTest class only covers non-numeric string inputs for shm_size. Add
two new test methods following the same pattern to cover boundary cases: one
test that sets shm_size to "0" and another that sets it to "-1", both asserting
that they trigger an InvalidShmSize error and that HostConfig.ShmSize is not set
on the container. Use similar setup and assertion patterns as the existing test
but with these specific invalid numeric values.

In `@tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs`:
- Around line 535-617: The test suite currently covers valid shm_size and
non-numeric invalid shm_size values, but is missing coverage for numeric invalid
boundary cases (zero and negative values) and the failure path when
StartContainerAsync returns false. Add two new test methods following the
pattern of the existing
ExecuteTask_WhenShmSizeIsInvalid_ExpectLogWarningAndNoException test: one that
verifies behavior when shm_size is set to numeric invalid values like 0 or
negative numbers (expecting appropriate error logging), and another that
verifies the PodmanPlugin correctly handles the case when
_podmanClient.Containers.StartContainerAsync returns false instead of true,
ensuring the task result status and any error handling reflects this container
startup failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 92e15090-66a3-4ff7-b1d9-1e471b76fec5

📥 Commits

Reviewing files that changed from the base of the PR and between 9e10e95 and 6f3309e.

📒 Files selected for processing (26)
  • src/Common/Miscellaneous/ValidationConstants.cs
  • src/Monai.Deploy.WorkflowManager.sln
  • src/TaskManager/Plug-ins/Docker/DockerPlugin.cs
  • src/TaskManager/Plug-ins/Docker/Keys.cs
  • src/TaskManager/Plug-ins/Docker/Logging/Log.cs
  • src/TaskManager/Plug-ins/Podman/AssemblyInfo.cs
  • src/TaskManager/Plug-ins/Podman/ContainerMonitorException.cs
  • src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs
  • src/TaskManager/Plug-ins/Podman/ContainerVolumeMount.cs
  • src/TaskManager/Plug-ins/Podman/IPodmanClientFactory.cs
  • src/TaskManager/Plug-ins/Podman/IPodmanContainerCreator.cs
  • src/TaskManager/Plug-ins/Podman/Keys.cs
  • src/TaskManager/Plug-ins/Podman/Logging/Log.cs
  • src/TaskManager/Plug-ins/Podman/Monai.Deploy.WorkflowManager.TaskManager.Podman.csproj
  • src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs
  • src/TaskManager/Plug-ins/Podman/SetPermissionException.cs
  • src/TaskManager/Plug-ins/Podman/Strings.cs
  • src/TaskManager/TaskManager/Extensions/TaskManagerExtensions.cs
  • src/TaskManager/TaskManager/Monai.Deploy.WorkflowManager.TaskManager.csproj
  • src/TaskManager/TaskManager/PluginStrings.cs
  • src/TaskManager/TaskManager/packages.lock.json
  • tests/UnitTests/TaskManager.Docker.Tests/DockerPluginTest.cs
  • tests/UnitTests/TaskManager.Podman.Tests/ContainerStatusMonitorTest.cs
  • tests/UnitTests/TaskManager.Podman.Tests/Monai.Deploy.WorkflowManager.TaskManager.Podman.Tests.csproj
  • tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs
  • tests/UnitTests/TaskManager.Podman.Tests/Usings.cs

Comment on lines +370 to +379
if (Event.TaskPluginArguments.ContainsKey(Keys.ShmSize))
{
if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize))
{
parameters.HostConfig.ShmSize = shmsize;
}
else
{
_logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce positive shm_size bounds, not just numeric parsing.

At Line 372, long.TryParse accepts 0 and negative values, but your own log contract says valid range starts at 1. This currently allows invalid values to be forwarded into HostConfig.ShmSize instead of being rejected/logged.

Suggested fix
-            if (Event.TaskPluginArguments.ContainsKey(Keys.ShmSize))
-            {
-                if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize))
-                {
-                    parameters.HostConfig.ShmSize = shmsize;
-                }
-                else
-                {
-                    _logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);
-                }
-            }
+            if (Event.TaskPluginArguments.ContainsKey(Keys.ShmSize))
+            {
+                if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize) && shmsize > 0)
+                {
+                    parameters.HostConfig.ShmSize = shmsize;
+                }
+                else
+                {
+                    _logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);
+                }
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (Event.TaskPluginArguments.ContainsKey(Keys.ShmSize))
{
if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize))
{
parameters.HostConfig.ShmSize = shmsize;
}
else
{
_logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);
}
if (Event.TaskPluginArguments.ContainsKey(Keys.ShmSize))
{
if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize) && shmsize > 0)
{
parameters.HostConfig.ShmSize = shmsize;
}
else
{
_logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Docker/DockerPlugin.cs` around lines 370 - 379, The
code at the `long.TryParse` check in the ShmSize handling block accepts zero and
negative values without validation. Add an additional check after successful
parsing to ensure the parsed shmsize value is greater than 0 before assigning it
to parameters.HostConfig.ShmSize. If the parsed value is not positive (less than
or equal to 0), log the error using _logger.InvalidShmSize with the invalid
value and skip the assignment, ensuring only valid positive sizes are accepted.

CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(taskDispatchEvent, nameof(taskDispatchEvent));
ArgumentNullException.ThrowIfNull(containerTimeout, nameof(containerTimeout));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify value-type null-guard misuse pattern and context
rg -n -C2 'ThrowIfNull\(containerTimeout' src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs
rg -n -C2 'TimeSpan containerTimeout' src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 840


🏁 Script executed:

# Inspect the method implementation to see how containerTimeout is used
sed -n '69,150p' src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs

# Check for similar null-guard patterns on other value types in the file
rg -n 'ThrowIfNull\(' src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 4224


Remove null-check and add timeout range validation.

containerTimeout is a TimeSpan (value type) and cannot be null, making line 78's ArgumentNullException.ThrowIfNull() a no-op. More critically, a zero or negative TimeSpan causes the retry loop (line 88) to skip entirely, silently timing out without error. Add explicit range validation instead:

-            ArgumentNullException.ThrowIfNull(containerTimeout, nameof(containerTimeout));
+            if (containerTimeout <= TimeSpan.Zero)
+            {
+                throw new ArgumentOutOfRangeException(nameof(containerTimeout), "Container timeout must be greater than zero.");
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ArgumentNullException.ThrowIfNull(containerTimeout, nameof(containerTimeout));
if (containerTimeout <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(containerTimeout), "Container timeout must be greater than zero.");
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs` at line 78, The
ArgumentNullException.ThrowIfNull call on containerTimeout is unnecessary
because TimeSpan is a value type that cannot be null. More importantly, this
misses the actual validation issue: a zero or negative TimeSpan will cause the
retry loop at line 88 to skip entirely, resulting in silent timeouts. Remove the
ArgumentNullException.ThrowIfNull call for containerTimeout and replace it with
explicit range validation that ensures containerTimeout is a positive TimeSpan
value, throwing an appropriate exception if the value is zero or negative.

Comment on lines +186 to +193
var relativePart = file.StartsWith(artifactsPath, StringComparison.Ordinal)
? file.Substring(artifactsPath.Length)
: file;
relativePart = relativePart.TrimStart('/');
var objectName = string.IsNullOrEmpty(destination.RelativeRootPath)
? relativePart
: destination.RelativeRootPath.TrimEnd('/') + "/" + relativePart;
_logger.UploadingFile(file, destination.Bucket, objectName);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize relative artifact paths using path APIs before forming object keys.

At Lines 186-193, manual substring/trim can leak platform separators (\) into object names. That can break key conventions and downstream retrieval consistency.

Suggested fix
-                    var relativePart = file.StartsWith(artifactsPath, StringComparison.Ordinal)
-                        ? file.Substring(artifactsPath.Length)
-                        : file;
-                    relativePart = relativePart.TrimStart('/');
+                    var relativePart = _fileSystem.Path.GetRelativePath(artifactsPath, file)
+                        .Replace('\\', '/');
                     var objectName = string.IsNullOrEmpty(destination.RelativeRootPath)
                         ? relativePart
                         : destination.RelativeRootPath.TrimEnd('/') + "/" + relativePart;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var relativePart = file.StartsWith(artifactsPath, StringComparison.Ordinal)
? file.Substring(artifactsPath.Length)
: file;
relativePart = relativePart.TrimStart('/');
var objectName = string.IsNullOrEmpty(destination.RelativeRootPath)
? relativePart
: destination.RelativeRootPath.TrimEnd('/') + "/" + relativePart;
_logger.UploadingFile(file, destination.Bucket, objectName);
var relativePart = _fileSystem.Path.GetRelativePath(artifactsPath, file)
.Replace('\\', '/');
var objectName = string.IsNullOrEmpty(destination.RelativeRootPath)
? relativePart
: destination.RelativeRootPath.TrimEnd('/') + "/" + relativePart;
_logger.UploadingFile(file, destination.Bucket, objectName);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/ContainerStatusMonitor.cs` around lines 186 -
193, The manual substring and trim operations on relativePart at lines 186-189
and the string concatenation forming objectName at lines 191-192 can leak
platform-specific path separators like backslashes into the object key name,
breaking object storage conventions. Replace the manual path manipulation logic
with C# Path APIs (such as Path.GetRelativePath or proper path normalization) to
ensure that both relativePart and objectName consistently use forward slashes as
separators, making the code platform-agnostic and preventing downstream
retrieval issues.

Comment on lines +28 to +30
public IDockerClient CreateClient(Uri podmanEndpoint)
{
return new DockerClientConfiguration(podmanEndpoint).CreateClient();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify endpoint flow and absence/presence of URI scheme validation.
rg -n -C3 --type=cs 'new Uri\(Event\.TaskPluginArguments\[Keys\.BaseUrl\]\)|CreateClient\(Uri podmanEndpoint\)|LocalPath|Scheme'

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 13811


🏁 Script executed:

# Search for PodmanClientFactory usage and any validation patterns
rg -n 'PodmanClientFactory|DockerClientConfiguration' --type=cs -B2 -A2 src/TaskManager/Plug-ins/Podman/

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 3039


🏁 Script executed:

# Look for unit tests related to PodmanClientFactory
fd -e '*Test*.cs' -e '*test*.cs' | xargs rg -l 'PodmanClientFactory|PodmanPlugin' 2>/dev/null | head -5

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 397


🏁 Script executed:

# Check if there's any validation in PodmanPlugin constructor or elsewhere
rg -n 'class PodmanPlugin|ArgumentException|ThrowIf' --type=cs src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs -B1 -A5

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 1710


🏁 Script executed:

# Check the unit tests to see if they validate endpoint formats
cat tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs | head -100

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 5049


🏁 Script executed:

# Search for any endpoint validation in the entire Podman plugin folder
rg -n 'Scheme|unix|LocalPath|ArgumentException' --type=cs src/TaskManager/Plug-ins/Podman/ -B1 -A1

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 333


🏁 Script executed:

# Check what endpoints are used in tests to understand expected format
rg -n 'BaseUrl.*=|CreateClient.*Uri' tests/UnitTests/TaskManager.Podman.Tests/ --type=cs -B1 -A1

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 2132


🏁 Script executed:

# Search for how Event.TaskPluginArguments[Keys.BaseUrl] is set in tests
rg -n 'GenerateTaskDispatchEvent|BaseUrl' tests/UnitTests/TaskManager.Podman.Tests/PodmanPluginTest.cs -A3 | head -50

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 3216


🏁 Script executed:

# Find where ValidateEvent is defined and what it checks
rg -n 'private void ValidateEvent' --type=cs src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs -A 30

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 1663


🏁 Script executed:

# Also check what happens with the BaseUrl validation in constructor
rg -n 'Keys.BaseUrl|ValidateEvent' --type=cs src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs -B2 -A2

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 1517


Validate Podman endpoint scheme before creating the client.

The ValidateEvent() method validates that BaseUrl is a well-formed absolute URI (line 93-96), but does not enforce the unix:// scheme required for Podman socket connections. Since this endpoint originates from unchecked task arguments, add scheme validation to CreateClient() to prevent unintended endpoint routing and enforce that only unix domain sockets are accepted.

🛡️ Proposed fix
         public IDockerClient CreateClient(Uri podmanEndpoint)
         {
+            ArgumentNullException.ThrowIfNull(podmanEndpoint);
+            if (!string.Equals(podmanEndpoint.Scheme, "unix", StringComparison.OrdinalIgnoreCase) ||
+                string.IsNullOrWhiteSpace(podmanEndpoint.LocalPath))
+            {
+                throw new ArgumentException("Podman endpoint must be a unix domain socket URI.", nameof(podmanEndpoint));
+            }
             return new DockerClientConfiguration(podmanEndpoint).CreateClient();
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public IDockerClient CreateClient(Uri podmanEndpoint)
{
return new DockerClientConfiguration(podmanEndpoint).CreateClient();
public IDockerClient CreateClient(Uri podmanEndpoint)
{
ArgumentNullException.ThrowIfNull(podmanEndpoint);
if (!string.Equals(podmanEndpoint.Scheme, "unix", StringComparison.OrdinalIgnoreCase) ||
string.IsNullOrWhiteSpace(podmanEndpoint.LocalPath))
{
throw new ArgumentException("Podman endpoint must be a unix domain socket URI.", nameof(podmanEndpoint));
}
return new DockerClientConfiguration(podmanEndpoint).CreateClient();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/IPodmanClientFactory.cs` around lines 28 -
30, The CreateClient method must validate the Podman endpoint scheme before
creating the client to prevent unintended endpoint routing. In the CreateClient
method, add validation logic to check that the podmanEndpoint parameter uses the
unix:// scheme, which is required for Podman socket connections. If the scheme
is not unix://, throw an appropriate exception with a descriptive error message
before proceeding to call CreateClient on the DockerClientConfiguration. This
ensures that only valid unix domain socket endpoints are accepted and prevents
misconfigured endpoints from being used.

Comment on lines +425 to +433
if (Event.TaskPluginArguments.ContainsKey(Keys.ShmSize))
{
if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize))
{
request.ShmSize = shmsize;
}
else
{
_logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject non-positive shm_size values before assigning.

Line 427 accepts any parsed long, including 0 and negatives, then sets request.ShmSize at Line 429. This contradicts the expected valid range and can produce invalid runtime requests.

Suggested fix
-                if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], out var shmsize))
+                if (long.TryParse(Event.TaskPluginArguments[Keys.ShmSize], NumberStyles.Integer, CultureInfo.InvariantCulture, out var shmsize) &&
+                    shmsize > 0)
                 {
                     request.ShmSize = shmsize;
                 }
                 else
                 {
                     _logger.InvalidShmSize(Event.TaskPluginArguments[Keys.ShmSize]);
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs` around lines 425 - 433,
After successfully parsing the ShmSize value with long.TryParse in the
conditional block, add validation to reject non-positive values (zero and
negative numbers) before assigning to request.ShmSize. When the parsed value is
not greater than zero, log an appropriate error using _logger (similar to the
existing InvalidShmSize call for parse failures) and skip the assignment. This
ensures only valid positive shm_size values are accepted and assigned to the
request object.

Comment on lines +482 to +486
var filePath = Path.Combine(inputContainerDirRoot, obj.FilePath.Replace(input.RelativeRootPath, "").TrimStart('/'));

// Task Manager Container Path of the Directory for this Input File.
var fileDirectory = Path.GetDirectoryName(filePath);
Directory.CreateDirectory(fileDirectory!);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Block path traversal when writing downloaded input artifacts.

Line 482 builds local paths from object keys using string replacement. A key containing traversal segments can escape inputContainerDirRoot and write outside the intended task directory.

Suggested fix
-                    var filePath = Path.Combine(inputContainerDirRoot, obj.FilePath.Replace(input.RelativeRootPath, "").TrimStart('/'));
+                    var relative = obj.FilePath.Replace(input.RelativeRootPath, string.Empty).TrimStart('/');
+                    var root = Path.GetFullPath(inputContainerDirRoot + Path.DirectorySeparatorChar);
+                    var filePath = Path.GetFullPath(Path.Combine(inputContainerDirRoot, relative));
+                    if (!filePath.StartsWith(root, StringComparison.Ordinal))
+                    {
+                        throw new InvalidOperationException($"Invalid object path '{obj.FilePath}' for input '{input.Name}'.");
+                    }
 
                     // Task Manager Container Path of the Directory for this Input File.
                     var fileDirectory = Path.GetDirectoryName(filePath);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs` around lines 482 - 486, The
path construction on line 482 in the PodmanPlugin.cs file is vulnerable to path
traversal attacks because it uses simple string replacement and trimming without
validating that the resulting path stays within the intended
inputContainerDirRoot directory. An attacker could craft object keys containing
traversal sequences like "../" that would escape the container directory and
write files outside the intended task directory. To fix this, after constructing
the filePath using Path.Combine, normalize it with Path.GetFullPath() and then
validate that the normalized path starts with the normalized
inputContainerDirRoot path. If the validation fails, reject the file path and
log an error to prevent the directory creation and file write operation.

Comment on lines +556 to +557
var process = Process.Start("chown", $"-R {Event.TaskPluginArguments[Keys.User]} {path}");
process.WaitForExit();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and examine lines around 556-557
find . -name "PodmanPlugin.cs" -type f

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 137


🏁 Script executed:

# Get the file location and examine the context
if [ -f "src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs" ]; then
  wc -l "src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs"
  echo "=== Lines 550-565 ===="
  sed -n '550,565p' "src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs" | cat -n
fi

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 1080


🏁 Script executed:

# Search for context on Event.TaskPluginArguments[Keys.User] and how it's populated
rg -n "TaskPluginArguments\[Keys.User\]" --context 5

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 5081


🏁 Script executed:

# Search for Keys.User definition and any validation
rg -n "Keys\.User" --context 3

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 4803


🏁 Script executed:

# Check for any input validation or sanitization related to user arguments
rg -n "chown" --context 10

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 4770


🏁 Script executed:

# Search for where Event.TaskPluginArguments is populated/assigned
rg -n "TaskPluginArguments\s*=" --context 3

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 32966


🏁 Script executed:

# Search for Keys class definition and any validation
rg -n "class Keys|enum Keys" --context 5

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 3329


🏁 Script executed:

# Look for any validation or sanitization of user input before it reaches SetPermission
rg -n "SetPermission" --context 5

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 12268


🏁 Script executed:

# Check if there's any documentation or validation around Keys.User in event/argument handling
rg -n "Keys\.User" -B5 -A2 --context 10 | head -100

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 8681


🏁 Script executed:

# Check the complete Keys.User definition in PodmanPlugin
cat src/TaskManager/Plug-ins/Podman/Keys.cs

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 3541


🏁 Script executed:

# Verify how Process.Start behaves with string interpolation in C#/.NET
# Check if there are any existing uses of ProcessStartInfo with ArgumentList in the codebase
rg -n "ArgumentList|ProcessStartInfo" --context 3

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 1125


🏁 Script executed:

# Check if there are timeout mechanisms for other process calls in the codebase
rg -n "WaitForExit\(" --context 2

Repository: Project-MONAI/monai-deploy-workflow-manager

Length of output: 1055


Use ProcessStartInfo with ArgumentList and timeout for chown execution.

Line 556 concatenates raw user input via string interpolation into the command arguments; if Keys.User contains whitespace or special characters, chown will misparse the arguments. Additionally, line 557's unbounded WaitForExit() can block indefinitely if the process hangs. The same issue exists in DockerPlugin.cs:505-506.

Suggested fix
-                    var process = Process.Start("chown", $"-R {Event.TaskPluginArguments[Keys.User]} {path}");
-                    process.WaitForExit();
+                    var psi = new ProcessStartInfo("chown")
+                    {
+                        RedirectStandardError = true,
+                        RedirectStandardOutput = true,
+                    };
+                    psi.ArgumentList.Add("-R");
+                    psi.ArgumentList.Add(Event.TaskPluginArguments[Keys.User]);
+                    psi.ArgumentList.Add(path);
+
+                    using var process = Process.Start(psi) ?? throw new SetPermissionException("Failed to launch chown process.");
+                    if (!process.WaitForExit(30_000))
+                    {
+                        process.Kill(entireProcessTree: true);
+                        throw new SetPermissionException("chown command timed out.");
+                    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/TaskManager/Plug-ins/Podman/PodmanPlugin.cs` around lines 556 - 557,
Replace the string concatenation approach used in the chown Process.Start call
with ProcessStartInfo that uses the ArgumentList property to properly pass the
user and path arguments separately, preventing parsing issues if the user input
contains whitespace or special characters. Additionally, add a timeout parameter
to the WaitForExit() call to prevent indefinite blocking if the process hangs.
Apply the same fix to the equivalent chown execution in DockerPlugin.

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.

2 participants