T08 · Insecure Dependencies
- Location
scripts/generate_plantuml.py:588- Finding
Mutable Docker Image Executed Without Version or Digest Pinning
- Content
View full analysis
/dev/null; plantuml -t{ext} /data/{src.name}", ] ``` The standard rendering path uses the same mutable image: ```python cmd = base + ["plantuml/plantuml:latest", f"-t{ext}", f"/data/{src.name}"] ``` ## Technical Analysis The preferred rendering backend executes `plantuml/plantuml:latest`. The `latest` tag is mutable and does not identify a cryptographically fixed container image. The effective code executed by the Skill can therefore change after the Skill package has been reviewed. If the image is not already available locally, Docker can retrieve the current image associated with this tag. A user or deployment process may also update the local image independently. A compromise of the upstream registry account, image build pipeline, or tag could consequently cause attacker-controlled container code to run during diagram rendering. The container receives a writable mount of the temporary rendering directory through `/data`. In CJK mode, it may additionally receive read-only mounts of host font directories. No network isolation, read-only root filesystem, capability dropping, or `no-new-privileges` restriction is configured. ### Attack Path 1. An attacker compromises the upstream container publishing process or otherwise causes a malicious image to be associated with `plantuml/plantuml:latest`. 2. The malicious image is pulled because it is absent locally, or the local mutable image is updated. 3. A user invokes `generate_plantuml.py`. 4. `convert_via_docker()` starts the unpinned image. 5. Attacker-controlled code r ...[truncated 1111 chars]- Remediation
View remediation
" "@sha256:" ) ``` 2. Establish a controlled update process that verifies the image digest, release provenance, changelog, and vulnerability scan results before changing the pinned reference. 3. Disable container networking unless rendering explicitly requires it: ```python base = [ "docker", "run", "--rm", "--network=none", "--cap-drop=ALL", "--security-opt=no-new-privileges", "--read-only", "-v", f"{mount}:/data", ] ``` 4. Where practical, mount the source read-only and use a separate narrowly scoped writable output directory. 5. Run the container as a non-root user and apply resource limits such as memory, process, and CPU limits. 6. Avoid mounting broad host font directories. Copy only the required font files into a dedicated temporary directory and mount that directory read-only. 7. Document and verify the expected image digest during startup so an unexpected local image cannot be used silently. ]]>
