Skip to content

[Bug]: Phase 4 — Docker Image Pinning, Non-Root Default, Markdown Injection (M-2, M-3, M-11) #33

@initializ-mk

Description

@initializ-mk

Component

forge-cli (commands, TUI wizard, runtime), forge-plugins (channel plugins, markdown converter)

Description

Three medium priority security hardening items identified via cross-reference with OpenClaw 2026.2.19 / 2026.2.20. These reduce attack surface but are not immediately exploitable.

Re-verified 2026-03-12 against latest main (commit 7d2148b). Key changes from original analysis:

  • M-11 expanded: Additional issues found — URLs in <a href> not URL-encoded (potential javascript: XSS), codeLang in class attribute not HTML-escaped.

Reference: FORGE-SECURITY-UPDATE.md — Phase 4

Steps to reproduce

M-2. Docker Base Images Not Pinned to SHA256

  • File: forge-cli/templates/Dockerfile.tmpl
  • Lines 2 and 12 use FROM {{.Runtime.Image}} — accepts mutable tags (e.g. python:3.11-slim) without requiring SHA256 digest pinning. Tag drift can silently change the base image.

M-3. Non-Root USER Not Default in Docker

  • File: forge-cli/templates/Dockerfile.tmpl (lines 69–72)
  • USER agent directive is conditional on {{- if .Runtime.User}}. Not enforced by default — containers run as root unless explicitly configured, expanding blast radius of container escapes.

M-11. Markdown/Content Injection — Quote and URL Escaping

  • File: forge-plugins/channels/markdown/markdown.go (lines 238–244)
  • escapeHTML() escapes &, <, > but NOT " or ' — potential attribute injection.
  • URLs in <a href="$2"> (line 105) are not URL-encoded — potential javascript: XSS.
  • codeLang in class="language-" (line 30) is not HTML-escaped.

Expected behavior

  • M-2: Runtime.Image validated to contain @sha256: digest. Warning or failure on mutable tags.
  • M-3: Non-root USER directive is unconditional by default. Opt-out via explicit Runtime.RootUser: true.
  • M-11: escapeHTML() escapes "&quot; and '&#39;. URLs validated. codeLang escaped.

Actual behavior

  • M-2: Mutable tags accepted without warning.
  • M-3: Containers run as root unless Runtime.User is explicitly set.
  • M-11: Quote characters, unvalidated URLs, and raw codeLang pass through unescaped.

Tasks

M-2. Docker Base Image SHA256 Pinning

  • Add validation that Runtime.Image contains @sha256: digest
  • Warn (or fail) if a mutable tag is used without a digest
  • Document recommended pinned images and update process

M-3. Non-Root USER Default

  • Make non-root USER directive unconditional (always add appuser)
  • Allow opt-out via explicit Runtime.RootUser: true config
  • Add test verifying generated Dockerfile includes USER directive by default

M-11. Escaping and Injection Prevention

  • Add "&quot; and '&#39; to escapeHTML()
  • URL-encode href attribute values or validate URLs don't contain javascript: scheme
  • Escape codeLang before inserting into class attribute
  • Add tests for quote characters, malicious URLs, and code language injection

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity vulnerability fixes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions