Back to skill

Security audit

Blog Forge

Security checks for vulnerabilities and agentic risk

Overview

BlogForge mostly does what it advertises, but it handles publishing credentials and can send them over plaintext HTTP, and it includes AI-detection-evasion wording that users should review carefully.

Install only if you are comfortable sending prompts, drafts, and API keys to the configured providers. Use HTTPS-only WordPress and Ghost URLs, prefer narrowly scoped publishing tokens, review drafts before making them public, and avoid using the humanization feature to hide required AI disclosure.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
blog-forge.js:933
Finding

Publishing credentials can be transmitted over plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: blog-forge.js:933-955, blog-forge.js:1124-1137, and the transport selection at blog-forge.js:1411-1422
Vulnerability Type: Failure to enforce encrypted transport for sensitive credentials
Risk Level: High

Vulnerable Code

javascript
const auth = Buffer.from(`${username}:${appPassword}`).toString('base64');

const parsedUrl = new URL(url);

const response = await this._httpRequest(
  {
    protocol: parsedUrl.protocol,
    hostname: parsedUrl.hostname,
    port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
    path: `${parsedUrl.pathname.replace(/\/$/, '')}/wp-json/wp/v2/posts`,
    method: 'POST',
    headers: {
      'Authorization': `Basic ${auth}`,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(postBody)
    }
  },
  postBody
);
javascript
const parsedUrl = new URL(url);

const response = await this._httpRequest(
  {
    protocol: parsedUrl.protocol,
    hostname: parsedUrl.hostname,
    port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
    path: `${parsedUrl.pathname.replace(/\/$/, '')}/ghost/api/admin/posts/`,
    method: 'POST',
    headers: {
      'Authorization': `Ghost ${jwt}`,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(postBody)
    }
  },
  postBody
);
javascript
_httpRequest(options, body) {
  return new Promise((resolve, reject) => {
    const protocol = options.protocol === 'http:'
      ? require('http')
      : require('https');

    const reqOptions = {
      hostname: options.hostname,
      port: options.port || (options.protocol === 'http:' ? 80 : 443),
      path: options.path,
      method: options.method || 'GET',
      headers: options.headers || {}
    };

    const req = protocol.request(reqOptions, (res) => {

Technical

...[truncated 2765 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject non-HTTPS remote publishing URLs before constructing any authorization header:

    javascript
    const parsedUrl = new URL(url);
    if (parsedUrl.protocol !== 'https:') {
      throw new Error('Remote publishing requires an HTTPS URL.');
    }
    
  2. If plaintext HTTP is required for local development, permit it only for loopback hosts such as 127.0.0.1, ::1, or localhost, and only through an explicit insecure-development option that defaults to disabled.

  3. Restrict accepted protocols to https: and the narrowly scoped loopback exception. Reject unsupported schemes, embedded URL credentials, malformed hosts, and unexpected ports.

  4. Consider allowing callers to configure an approved hostname list so credentials cannot be redirected to arbitrary destinations.

  5. If redirect support is later introduced, never forward authorization headers across origins or from HTTPS to HTTP.

  6. Use narrowly scoped publishing credentials and rotate any WordPress or Ghost credentials that may previously have been transmitted over HTTP.

  7. Add automated tests confirming that remote http:// WordPress and Ghost URLs are rejected before a request is opened.

T09 · Insecure Skill Coding Practices

Error
Location
src/blog-forge.js:933
Finding

Duplicated publishing implementation permits plaintext credential transmission

Content
View full analysis

Vulnerability Details

File Location: src/blog-forge.js:933-955, src/blog-forge.js:1124-1137, and the transport selection at src/blog-forge.js:1411-1422
Vulnerability Type: Failure to enforce encrypted transport for sensitive credentials
Risk Level: High

Vulnerable Code

javascript
const auth = Buffer.from(`${username}:${appPassword}`).toString('base64');

const parsedUrl = new URL(url);

const response = await this._httpRequest(
  {
    protocol: parsedUrl.protocol,
    hostname: parsedUrl.hostname,
    port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
    path: `${parsedUrl.pathname.replace(/\/$/, '')}/wp-json/wp/v2/posts`,
    method: 'POST',
    headers: {
      'Authorization': `Basic ${auth}`,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(postBody)
    }
  },
  postBody
);
javascript
const parsedUrl = new URL(url);

const response = await this._httpRequest(
  {
    protocol: parsedUrl.protocol,
    hostname: parsedUrl.hostname,
    port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
    path: `${parsedUrl.pathname.replace(/\/$/, '')}/ghost/api/admin/posts/`,
    method: 'POST',
    headers: {
      'Authorization': `Ghost ${jwt}`,
      'Content-Type': 'application/json',
      'Content-Length': Buffer.byteLength(postBody)
    }
  },
  postBody
);
javascript
const protocol = options.protocol === 'http:'
  ? require('http')
  : require('https');

const reqOptions = {
  hostname: options.hostname,
  port: options.port || (options.protocol === 'http:' ? 80 : 443),
  path: options.path,
  method: options.method || 'GET',
  headers: options.headers || {}
};

const req = protocol.request(reqOptions, (res) => {

Technical Analysis

This file duplicates the vulnerable implementation in the project-root blog-forge.js. WordPress ...[truncated 1388 chars]

Remediation
View remediation

Remediation Suggestions

Apply the same fix to this source copy and prevent the two implementations from diverging:

  1. Require https: for every remote WordPress and Ghost endpoint.
  2. Allow HTTP only for explicitly enabled loopback development endpoints.
  3. Reject embedded URL credentials, unsupported protocols, and unapproved destination hosts.
  4. Ensure authorization headers are never forwarded to a different origin or downgraded to HTTP.
  5. Add tests for protocol validation and credential handling.
  6. Maintain a single canonical implementation and have the package entry point re-export it, rather than retaining two independently editable copies.
  7. Rotate credentials that may have been exposed through earlier plaintext publishing requests.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The code substantially matches the declared primary purpose: it generates blog content with Anthropic/OpenAI/Ollama, computes SEO/readability outputs, provides humanization, and supports publishing. However, the description declares no permissions, while the code clearly performs outbound network requests to multiple third-party APIs and consumes sensitive credentials/tokens (including environment API keys, Medium token, WordPress credentials, and Ghost admin API key). Because publishing and model access require external network/resource access that is not reflected in the declared permissions, this is a description/behavior mismatch on capabilities/resources, even though the functional description is otherwise accurate.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The publish() capability transmits generated content plus platform credentials/tokens to external blogging services without any built-in confirmation or safety interlock. In an agent context, this can lead to unintended publication, credential misuse, or disclosure of sensitive draft material to third-party platforms.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The humanize() capability is explicitly described as reducing AI-detection signals and introducing stylistic changes to evade classifiers. That materially increases misuse potential for academic fraud, spam, deceptive marketing, and policy circumvention, and the evasive framing makes the skill context more dangerous rather than less.

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 · blog-forge.js (reported line 648)May include surrounding context.

js
if (!key) {
        throw new Error(
          'ANTHROPIC_API_KEY environment variable is required for Anthropic models. ' +
          'Set it in your environment or .env file.'
        );
      }
      return key;

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 · blog-forge.js (reported line 659)May include surrounding context.

js
if (!key) {
        throw new Error(
          'ANTHROPIC_API_KEY environment variable is required for Anthropic models. ' +
          'Set it in your environment or .env file.'
        );
      }
      return key;

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 · src/blog-forge.js (reported line 648)May include surrounding context.

js
if (!key) {
        throw new Error(
          'ANTHROPIC_API_KEY environment variable is required for Anthropic models. ' +
          'Set it in your environment or .env file.'
        );
      }
      return key;

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 · src/blog-forge.js (reported line 659)May include surrounding context.

js
if (!key) {
        throw new Error(
          'ANTHROPIC_API_KEY environment variable is required for Anthropic models. ' +
          'Set it in your environment or .env file.'
        );
      }
      return key;

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

Advertising 'Anti-AI detection' functionality suggests the tool is intended to help users evade systems that identify AI-generated content. That increases abuse potential for academic dishonesty, deceptive marketing, policy evasion, and reputational harm, especially because the surrounding context emphasizes producing human-sounding content for publication.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README states that the software does not transmit data externally unless explicitly configured, yet the product description highlights support for remote AI providers such as Anthropic and OpenAI. This can mislead users about where prompts, drafts, and possibly sensitive content are sent, creating privacy and compliance risk if they assume processing is local by default or fail to understand outbound data flows.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The generate() flow sends user-supplied topic/content context to third-party AI providers (Anthropic, OpenAI, or local Ollama) with no built-in consent, disclosure, or data-classification guardrails. This is dangerous because users may unintentionally submit sensitive or proprietary material to external services, creating privacy, compliance, or contractual exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code explicitly advertises 'humanization' to reduce AI-detection signals, which is functionality aimed at concealing AI origin rather than improving safety or quality alone. That can facilitate deceptive or policy-evasive use, especially in academic, editorial, or platform-governed environments where AI disclosure matters.

Content

No source excerpt is available for this finding.

Ssd 2

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This functionality is explicitly positioned to make AI-generated content appear more human and reduce detection signals, which materially increases misuse potential for impersonation or undisclosed synthetic content. In a blog-generation and direct-publishing skill, that context makes the feature more dangerous because it pairs concealment with easy distribution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The generate() workflow sends user-supplied topic/content context to third-party AI providers (Anthropic, OpenAI, or local Ollama) without any built-in consent, disclosure, or data-handling guardrails. In a content-generation skill, users may paste proprietary, client, or regulated material into prompts, so silent transmission creates a real privacy and compliance risk even if the network behavior is functionally intended.

Content

No source excerpt is available for this finding.

Ssd 2

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The same humanization feature operationalizes evasion through paraphrasing, rhythm variation, contractions, and deliberate 'imperfections' specifically to appear less machine-generated. In a blog automation skill, that supports deceptive mass content generation and makes abuse at scale easier, even if it is not a direct system-compromise bug.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The publish() feature transmits generated content plus authentication material to external publishing platforms without any confirmation, warning, or trust validation. This is risky because publishing targets are user-controlled URLs for WordPress/Ghost, so mistakes or abuse can send content and credentials to unintended destinations.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
70% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

md
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for

Static analysis

No suspicious patterns detected.