T09 · Insecure Skill Coding Practices
- 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 atblog-forge.js:1411-1422
Vulnerability Type: Failure to enforce encrypted transport for sensitive credentials
Risk Level: HighVulnerable 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
-
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.'); } -
If plaintext HTTP is required for local development, permit it only for loopback hosts such as
127.0.0.1,::1, orlocalhost, and only through an explicit insecure-development option that defaults to disabled. -
Restrict accepted protocols to
https:and the narrowly scoped loopback exception. Reject unsupported schemes, embedded URL credentials, malformed hosts, and unexpected ports. -
Consider allowing callers to configure an approved hostname list so credentials cannot be redirected to arbitrary destinations.
-
If redirect support is later introduced, never forward authorization headers across origins or from HTTPS to HTTP.
-
Use narrowly scoped publishing credentials and rotate any WordPress or Ghost credentials that may previously have been transmitted over HTTP.
-
Add automated tests confirming that remote
http://WordPress and Ghost URLs are rejected before a request is opened.
-
