Back to skill

Security audit

Obsidian Cloudflare Pages

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent publishing purpose, but its CLI uses unsafe shell commands, configurable destructive deletion, plaintext credential storage, and unpinned remote tooling that merit Review before installation.

Treat this as a Review item rather than a normal install. Only run it in a dedicated workspace with backed-up content, a narrowly scoped Cloudflare Pages token, and no valuable files reachable through configured publish paths. Avoid enabling Basic Auth until password handling is changed to use Cloudflare secrets or another managed secret store, and prefer a fixed, reviewed version of Quartz and Wrangler before deployment.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
bin/publishmd-cf.js:40
Finding

Configuration-Controlled Shell Command Injection

Content
View full analysis
`--exclude '${f}/'`) .join(' '); sh(`rsync -av --exclude '.obsidian/' --exclude '*.canvas' ${excludes} "${src}/" "${dest}/${folder}/"`); } ``` ```js const project = cfg.cloudflare.projectName; const branch = cfg.cloudflare.branch || 'main'; const tokenEnv = cfg.cloudflare?.apiTokenEnv || 'CLOUDFLARE_API_TOKEN'; const accountEnv = cfg.cloudflare?.accountIdEnv || 'CLOUDFLARE_ACCOUNT_ID'; const token = process.env[tokenEnv] || ''; const accountId = process.env[accountEnv] || ''; if (!token) throw new Error(`Missing Cloudflare token env var: ${tokenEnv}`); const envPrefix = [ `CLOUDFLARE_API_TOKEN="${token.replace(/"/g, '\\"')}"`, accountId ? `CLOUDFLARE_ACCOUNT_ID="${accountId.replace(/"/g, '\\"')}"` : '' ].filter(Boolean).join(' '); sh(`${envPrefix} npx wrangler pages deploy public --project-name "${project}" --branch "${branch}"`, workspaceDir); ``` ### Technical Analysis The `sh` helper sends complete command strings to `execSync`, which invokes a shell. Multiple values originating from editable configuration, wizard input, or environment variables are interpolated into these command strings. Shell quoting is incomplete and ...[truncated 2166 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
bin/publishmd-cf.js:245
Finding

Unvalidated Paths Permit Directory Traversal and Destructive Deletion

Content
View full analysis
`--exclude '${f}/'`) .join(' '); sh(`rsync -av --exclude '.obsidian/' --exclude '*.canvas' ${excludes} "${src}/" "${dest}/${folder}/"`); } ``` ```js function setupProject() { const cfg = loadConfig(); const workspaceDir = expandHome(cfg.publish.workspaceDir); fs.mkdirSync(workspaceDir, { recursive: true }); const hasQuartz = fs.existsSync(path.join(workspaceDir, 'quartz.config.ts')); if (hasQuartz) { console.log('Quartz project already initialized ✅'); return; } console.log(`Initializing Quartz project in: ${workspaceDir}`); // Preferred path (older Quartz bootstrap flow). let bootstrapOk = false; try { if (!fs.existsSync(path.join(workspaceDir, 'package.json'))) { sh('npm init -y', workspaceDir); } sh('npx quartz create', workspaceDir); bootstrapOk = fs.existsSync(path.join(workspaceDir, 'quartz.config.ts')); } catch { bootstrapOk = false; } // Fallback path: clone Q ...[truncated 2875 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
bin/publishmd-cf.js:438
Finding

Basic Authentication Credentials Are Echoed and Stored in Plaintext

Content
View full analysis
s.trim()).filter(Boolean), excludeFolders: excludeFolders.split(',').map((s) => s.trim()).filter(Boolean), requireFrontmatterPublish: /^y(es)?$/i.test(requirePublish) }, publish: { workspaceDir, contentDir }, site: { generator: 'quartz', title, baseUrl, theme, showBacklinks: /^y(es)?$/i.test(showBacklinks), branding: { rootSourceFolder, clippingsIndexLabel, rootIndexLabel, sidebarTitleHtml } }, cloudflare: { projectName, branch, productionDomain: domain, apiTokenEnv, accountIdEnv, basicAuth: { enabled: /^y(es)?$/i.test(authEnabled), username: authUsername, password: authPassword } } }; saveConfig(cfg); ``` ```js if (!auth.username || !auth.password) { throw new Error('basicAuth is enabled but username/password is missing in config.cloudflare.basicAuth'); } fs.mkdirSync(fnDir, { recursive: true }); const middleware = `const USER = ${JSON.stringify(auth.username)};\nconst PASS = ${JSON.stringify(auth.password)};\n\nfunction unauthorized() {\n return new Response("Authentication required", {\n status: 401,\n headers: {\n "WWW-Au ...[truncated 2426 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Error
Location
bin/publishmd-cf.js:283
Finding

Unpinned Remote Dependencies and Repository Code Are Executed

Content
View full analysis
0) { sh(`rm -rf "${workspaceDir}"/*`); } sh(`git clone https://github.com/jackyzha0/quartz.git "${workspaceDir}"`); sh('npm i', workspaceDir); bootstrapOk = fs.existsSync(path.join(workspaceDir, 'quartz.config.ts')); } ``` ```js function build() { const cfg = loadConfig(); const workspaceDir = expandHome(cfg.publish.workspaceDir); sh('npx quartz build', workspaceDir); ``` ```js const envPrefix = [ `CLOUDFLARE_API_TOKEN="${token.replace(/"/g, '\\"')}"`, accountId ? `CLOUDFLARE_ACCOUNT_ID="${accountId.replace(/"/g, '\\"')}"` : '' ].filter(Boolean).join(' '); sh(`${envPrefix} npx wrangler pages deploy public --project-name "${project}" --branch "${branch}"`, workspaceDir); ``` ### Technical Analysis The setup, build, and deployment workflow executes third-party code without a fixed reviewed version: - `npx quartz create` does not specify a package version. - `git clone` retrieves the repository's current default branch rather than a reviewed commit. - `npm i` installs the dependency graph and may execute package lifecycle scripts. - `npx quartz build` and `n ...[truncated 1642 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description understates the skill's behavior relative to the documented capabilities: local config discovery, home-directory reads, project cloning, package installation, HTML injection/branding changes, and basic-auth credential handling. This mismatch is dangerous because users may consent to a simple publishing action while the skill performs broader filesystem, network, and credential-affecting operations.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 120)May include surrounding context.

Option B: skill-local env file (recommended for this skill)

bash
cp skills/obsidian-cloudflare-pages/.env.example skills/obsidian-cloudflare-pages/.env
# then edit .env

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 121)May include surrounding context.

Option B: skill-local env file (recommended for this skill)

bash
cp skills/obsidian-cloudflare-pages/.env.example skills/obsidian-cloudflare-pages/.env
# then edit .env

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 83)May include surrounding context.

Option B: skill-local env file (recommended for this skill)

bash
cp skills/obsidian-cloudflare-pages/.env.example skills/obsidian-cloudflare-pages/.env
# then edit .env

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 121)May include surrounding context.

Option B: skill-local env file (recommended for this skill)

bash
cp skills/obsidian-cloudflare-pages/.env.example skills/obsidian-cloudflare-pages/.env
# then edit .env

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 122)May include surrounding context.

Option B: skill-local env file (recommended for this skill)

bash
cp skills/obsidian-cloudflare-pages/.env.example skills/obsidian-cloudflare-pages/.env
# then edit .env

Credential Access

High
Category
Privilege Escalation
Confidence
74% confidence
Finding

The statement that the CLI auto-loads a skill-local .env means the tool will automatically ingest secrets from disk at runtime. In an agent environment, automatic secret loading increases the chance of unintended credential use during runs and makes secret access less explicit to the user.

Content

Scanner excerpt · SKILL.md (reported line 84)May include surrounding context.

bash
cp skills/obsidian-cloudflare-pages/.env.example skills/obsidian-cloudflare-pages/.env
# then edit .env

The CLI auto-loads skills/obsidian-cloudflare-pages/.env (without overriding existing shell env vars).

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 119)May include surrounding context.

md
node bin/publishmd-cf.js init

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 120)May include surrounding context.

md
node bin/publishmd-cf.js init

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 123)May include surrounding context.

md
node bin/publishmd-cf.js init

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · bin/publishmd-cf.js (reported line 32)May include surrounding context.

js
}

// Load skill-local env vars if present (without overriding shell env)
loadDotEnv(path.join(root, '.env'));

function expandHome(p) {
  return p?.startsWith('~/') ? path.join(os.homedir(), p.slice(2)) : p;

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The script constructs a shell command rm -rf "${dest}"/* using configuration-derived path components and executes it via execSync. Even though the path is quoted, shell execution of destructive commands against configurable targets creates both severe data-loss risk and a tool-parameter-abuse primitive if path validation fails or assumptions about shell expansion break.

Content

Scanner excerpt · bin/publishmd-cf.js (reported line 261)May include surrounding context.

js
if (!cfg.source.includeFolders?.length) throw new Error('includeFolders is empty');

  fs.mkdirSync(dest, { recursive: true });
  sh(`rm -rf "${dest}"/*`);

  for (const folder of cfg.source.includeFolders) {
    const src = path.join(vaultPath, folder);

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The fallback bootstrap path runs rm -rf "${workspaceDir}"/* through the shell using a user-configurable directory. This grants a highly destructive primitive to configuration input and can wipe arbitrary filesystem locations if the configured workspace is unsafe, mistyped, or adversarially supplied.

Content

Scanner excerpt · bin/publishmd-cf.js (reported line 324)May include surrounding context.

js
if (!bootstrapOk) {
    console.log('Falling back to git clone bootstrap for Quartz...');
    if (fs.readdirSync(workspaceDir).length > 0) {
      sh(`rm -rf "${workspaceDir}"/*`);
    }
    sh(`git clone https://github.com/jackyzha0/quartz.git "${workspaceDir}"`);
    sh('npm i', workspaceDir);

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 27)May include surrounding context.

md
Commands:

- `init` — create config from example
- `wizard` — interactive setup
- `setup-project` — initialize Quartz workspace if needed
- `doctor` — dependency/env/path checks

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill requests or relies on environment-based capabilities for Cloudflare credentials but does not declare any explicit tool scope or permissions boundaries in the skill metadata. In an agent setting, undeclared capability use reduces transparency and can let the skill access secrets or runtime features that users did not clearly authorize.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

Using npx quartz without a pinned version allows retrieval of whatever package version is current at execution time, creating a supply-chain risk. A malicious or compromised upstream release could execute arbitrary code during bootstrap/build in the user's environment.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

The skill encourages persistent storage of long-lived Cloudflare API credentials in shell profiles or local env files, which extends secret lifetime across sessions. If the host or agent context is later compromised, these persisted credentials could be reused to deploy or alter Cloudflare Pages and possibly DNS settings.

Content

Scanner excerpt · SKILL.md (reported line 61)May include surrounding context.

md
## Cloudflare API token setup (recommended)

Create a Cloudflare API token with at least:
- **Account → Cloudflare Pages:Edit**
- (Optional) **Zone → DNS:Edit** if you want DNS automation elsewhere

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The sync step performs rm -rf "${dest}"/* before copying content, with no confirmation or safety guard beyond config-derived paths. If workspaceDir or contentDir is misconfigured, this can irreversibly delete arbitrary local files and is especially dangerous because the path is user-configurable and later passed to a shell command.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
87% confidence
Finding

The script executes npx quartz create without pinning a version or verifying package integrity, so the exact code fetched and run can change over time. Because this is part of project bootstrap, a compromised or malicious upstream package/version could execute arbitrary code on the operator's machine with the user's privileges.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

During fallback setup, the script wipes the entire configured workspace with rm -rf if the directory is non-empty, without explicit user approval. Because workspaceDir is configurable, a mistake or maliciously influenced config could cause broad data loss unrelated to the Quartz project.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
84% confidence
Finding

The build step runs npx quartz build without pinning the Quartz version, which allows execution of whatever version NPX resolves at runtime. In a publishing automation context, that creates a supply-chain execution path where upstream compromise or unexpected updates can run arbitrary code locally and alter published output.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The skill adds optional HTTP Basic Auth middleware and manages access credentials directly in its config/workspace, expanding scope from content publishing into access-control handling. That increases the attack surface and encourages storing secrets in less protected locations, which is risky in a publishing utility whose primary role does not require credential management logic.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The generated middleware embeds the basic-auth username and password directly into a file on disk, creating plaintext credential storage in the workspace. Those credentials may be exposed through backups, source control mistakes, local compromise, or accidental publication of project files.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding

The deploy step invokes npx wrangler pages deploy without pinning Wrangler to a known version. Since this command runs with Cloudflare credentials in scope, an unexpected or malicious package version could exfiltrate tokens or deploy altered content.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The example prompt “Sync, build, and deploy to Cloudflare Pages.” describes a broad imperative rather than a narrowly scoped activation phrase or constrained invocation condition. In a skill description, this can blur when the skill should activate versus when a user is just discussing deployment steps.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
bin/publishmd-cf.js:44