Security report for Traviseric/teneo-marketplace
Hello maintainers,
I am a security researcher studying security risks in vibe-coded software. During this research, I reviewed this repository and identified the findings below. These findings were identified in commit 1dee886. Each finding has undergone human analysis, but the report may still contain mistakes or incomplete interpretations. Please review this report and apply the necessary security fixes.
This report contains 19 confirmed findings identified during security review. Validation details below distinguish code evidence from runtime observations and note any limitations.
Summary
| ID |
Severity |
Category |
Affected area |
| SEC-001 |
Critical |
Cryptographic Failures |
marketplace/frontend/brands/teneo/config.json:7
marketplace/backend/config/network.js:49 |
| SEC-002 |
High |
Cryptographic Failures |
marketplace/backend/middleware/teneoAuth.js:112-119
marketplace/backend/middleware/teneoAuth.js:125-137
marketplace/backend/middleware/teneoAuth.js:112-119,125-137 |
| SEC-003 |
High |
Injection |
marketplace/frontend/admin-dashboard.html |
| SEC-004 |
High |
Injection |
marketplace/backend/routes/storeBuilder.js |
| SEC-005 |
High |
Authentication Failures |
marketplace/backend/server.js
marketplace/backend/routes/adminRoutes.js |
| SEC-006 |
High |
Cryptographic Failures |
marketplace/frontend/cart.js
marketplace/frontend/checkout.html
marketplace/frontend/brands/teneo/brand-manager.html
marketplace/frontend/manage-books.html
marketplace/frontend/admin/lulu-manager.html
marketplace/frontend/js/*.js |
| SEC-007 |
High |
Injection |
marketplace/frontend/brands/master-templates/purchase-success.html |
| SEC-008 |
High |
Injection |
marketplace/frontend/js/product-page-enhanced.js:386
marketplace/frontend/js/success-stories.js:139,144,152,212
marketplace/frontend/brands/master-templates/main.js:368
marketplace/frontend/js/publisher-profile.js:106
funnel-module/frontend/js/course-player.js:339,347
marketplace/frontend/js/success-stories.js:144, 152, 212
publisher-profile.js:106
course-player.js:339,347
success-stories.js:139,144,152 |
| SEC-009 |
High |
Injection |
marketplace/frontend/js/marketplace.js |
| SEC-010 |
High |
Injection |
marketplace/backend/services/storeRendererService.js
marketplace/backend/server.js |
| SEC-011 |
High |
Insecure Design |
marketplace/backend/routes/checkout.js
marketplace/backend/server.js
server.js |
| SEC-012 |
High |
Broken Access Control |
marketplace/backend/routes/checkout.js |
| SEC-013 |
Medium |
Broken Access Control |
marketplace/backend/routes/emailMarketing.js:412-434
marketplace/backend/routes/emailMarketing.js:412–434 |
| SEC-014 |
Medium |
Injection |
marketplace/backend/routes/emailMarketing.js:458-462
marketplace/backend/routes/emailTracking.js
marketplace/backend/routes/emailMarketing.js:458, 461 |
| SEC-015 |
Medium |
Broken Access Control |
marketplace/backend/server.js
marketplace/backend/routes/auth.js |
| SEC-016 |
Medium |
Broken Access Control |
funnel-module/frontend/js/funnel-builder.js |
| SEC-017 |
Medium |
Cryptographic Failures |
marketplace/backend/database/database.js:212
marketplace/backend/database/init.js:126 |
| SEC-018 |
Medium |
Injection |
funnel-module/frontend/js/funnel-builder.js:934 |
| SEC-019 |
Medium |
Mishandling of Exceptional Conditions |
marketplace/backend/routes/storefront.js
marketplace/backend/routes/merchantFulfillment.js
marketplace/backend/routes/adminRoutes.js
routes/storefront.js
routes/merchantFulfillment.js
routes/adminRoutes.js |
1. Remove Hardcoded RSA Private Key from Source Control
ID: SEC-001
Severity: Critical
Category: Cryptographic Failures
Affected code: marketplace/frontend/brands/teneo/config.json:7, marketplace/backend/config/network.js:49
Impact
The RSA private key is exposed in a public, git-tracked configuration and can be used to forge federation signatures. Existing copies remain compromised even after removing the current file.
Technical details
- The Teneo brand configuration contains a
private_key value, and the supplied validation record identifies it as a full PKCS8 PEM RSA-2048 private key.
marketplace/backend/config/network.js:49 assigns config.private_key to this.privateKey for federation-message signing.
Relevant code
marketplace/frontend/brands/teneo/config.json:1-10
{
"network_enabled": false,
"share_catalog": false,
"accept_referrals": false,
"referral_percentage": 0,
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDBBNc1QuYFCesEXFvNq\nElu+/vm4xqJjItamekYKwYpA5llifPCS8rem4cj+DivUwXrFum8x6HcnlZgRrpcQ\nQ9M+XIFeDYwwsHYLgexpSL/OuxKB4bXrbWI7da+vDOTYtH4RDN5lT3t7cheEIaHQ\nh3kOP/luF25xz5ZaNvLBBG/PTSIHRsFnvu1AObVhPhpxNCQej7t7WGsQGwCrr2OH\nT2QXovMH+DTmJo4q4RbbYyxyKr2iHHu4egAsdc5IXsKClTcBWMGQU5+0VNcX3sWG\n5dFKF0eqQNiLMXjOBPMPn8wUzyl5jNVbdto3yDgYjQYre4NBKhk8trJAs+k3FPW1\n6wIDAQAB\n-----END PUBLIC KEY-----\n",
"private_key": "[REDACTED_SECRET]",
"network_peers": [],
"trusted_stores": []
}
Validation
Code evidence
-
marketplace/frontend/brands/teneo/config.json:7 contains the private_key configuration field.
-
marketplace/backend/config/network.js:49 loads the configured private key directly.
Limitations
-
The provided code snippet redacts the secret value, so the exact key material is not reproduced here.
-
No live probe was performed; the finding is based on repository evidence.
Recommended remediation
Remove the private key from the repository and purge it from git history. Revoke or rotate the exposed key, then load its replacement through runtime environment or secret-management injection.
2. Encrypt or Avoid Persisting Raw Access Tokens in Database
ID: SEC-002
Severity: High
Category: Cryptographic Failures
Affected code: marketplace/backend/middleware/teneoAuth.js:112-119, marketplace/backend/middleware/teneoAuth.js:125-137, marketplace/backend/middleware/teneoAuth.js:112-119,125-137
Impact
A database compromise exposes usable Teneo bearer tokens, allowing token replay against authenticated functionality.
Technical details
updateStoredToken inserts the supplied token directly into teneo_auth_tokens.access_token.
getStoredToken retrieves the token as plaintext; the supplied record identifies no hashing or encryption on this path.
Relevant code
marketplace/backend/middleware/teneoAuth.js:106-125
}
console.error('Error validating book ownership:', error.message);
throw error;
}
}
static async updateStoredToken(userId, token) {
const query = `
INSERT OR REPLACE INTO teneo_auth_tokens (user_id, access_token, updated_at)
VALUES (?, ?, CURRENT_TIMESTAMP)
`;
try {
await db.run(query, [userId, token]);
} catch (error) {
console.error('Error storing Teneo token:', error);
}
}
static async getStoredToken(userId) {
Validation
Code evidence
-
marketplace/backend/middleware/teneoAuth.js:112–119 executes INSERT OR REPLACE ... VALUES (?, ?, ...) with [userId, token].
-
The supplied validation record identifies teneo_auth_tokens.access_token as TEXT NOT NULL and contrasts this with encrypted merchant credentials elsewhere.
Limitations
-
The deployed /api/auth/status endpoint returned 404, so database behavior was not runtime-tested.
Recommended remediation
Store a keyed digest or managed-key encryption of access tokens, compare or decrypt only when required, apply an appropriate token lifetime, and prevent token values from being logged.
3. Fix Stored XSS in Admin Dashboard Order Rendering
ID: SEC-003
Severity: High
Category: Injection
Affected code: marketplace/frontend/admin-dashboard.html
Impact
Attacker-controlled order data can execute JavaScript in an administrator's browser when the admin dashboard renders affected orders.
Technical details
renderOrders() interpolates database fields such as customer_email, book_title, and payment_status into an innerHTML template without escaping.
- The supplied record states that
customer_name and other order fields originate from checkout-controlled data; an escapeHtml() helper exists in the file but is not applied to this renderer.
Validation
Code evidence
-
The supplied validation record locates the vulnerable interpolation in marketplace/frontend/admin-dashboard.html around lines 2923–2943.
-
The same record identifies an existing escapeHtml() helper used by another renderer, while renderOrders() leaves the listed fields unescaped.
Limitations
-
No code snippet was supplied for the dashboard renderer.
-
The deployed API was reported as unavailable, so the end-to-end injection path was not runtime-tested.
Recommended remediation
Render untrusted order fields with textContent or equivalent safe DOM APIs. If HTML is required, apply context-appropriate allowlist sanitization before insertion.
4. Escape User-Supplied Values in Store Builder HTML Template Renderer
ID: SEC-004
Severity: High
Category: Injection
Affected code: marketplace/backend/routes/storeBuilder.js
Impact
Merchant-controlled storefront configuration can produce HTML containing script payloads, causing XSS for users who view the rendered or saved storefront.
Technical details
POST /api/store-builder/render accepts a caller-supplied config and passes it to renderStorePage without an authentication gate shown in the supplied route.
- The renderer uses
String(val) and raw template interpolation rather than HTML encoding values before constructing HTML.
Relevant code
marketplace/backend/routes/storeBuilder.js:56-77
}
});
// POST /api/store-builder/render
// Body: { "config": <StoreConfig> }
// Returns: { success: true, html: "<html>..." }
router.post('/render', (req, res) => {
const { config } = req.body;
if (!config) return res.status(400).json({ error: 'config required' });
try {
const html = renderStorePage(config);
res.json({ success: true, html });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// POST /api/store-builder/generate-and-render
// Single call: brief → config → HTML
// Body: { "brief": "..." }
// Returns: { success: true, config, html }
router.post('/generate-and-render', async (req, res) => {
Validation
Code evidence
-
marketplace/backend/routes/storeBuilder.js:62–71 accepts req.body.config and returns the result of renderStorePage(config).
-
The supplied validation record identifies storeRendererService.js:35 and inline interpolations around lines 178, 252, 254, and 268 as unescaped HTML sinks.
Limitations
-
The deployed render endpoint returned 404, so exploitation was not confirmed against the deployed instance.
Recommended remediation
HTML-escape all configuration values before insertion, or use safe DOM/template APIs. Sanitize explicitly allowed rich text with a server-side allowlist, and require authorization for store creation or rendering where appropriate.
5. Configure Persistent Session Store to Replace Insecure MemoryStore Default
ID: SEC-005
Severity: High
Category: Authentication Failures
Affected code: marketplace/backend/server.js, marketplace/backend/routes/adminRoutes.js
Impact
Sessions are held in process memory and disappear on restart. The admin login path also preserves the existing session identifier while granting admin state, creating a session-fixation risk.
Technical details
express-session is configured without a store option in server.js, so the default MemoryStore is used.
adminRoutes.js:121–123 sets req.session.isAdmin = true without calling req.session.regenerate(); the supplied record identifies regeneration in several other authentication paths.
Relevant code
marketplace/backend/server.js:1-30
// marketplace/backend/server.js
require('dotenv').config();
// Environment variable validation
function validateEnvironment() {
if (process.env.NODE_ENV === 'production') {
const fatal = [];
if (!process.env.ADMIN_PASSWORD_HASH) {
fatal.push('ADMIN_PASSWORD_HASH must be set in production. Run: node scripts/generate-password-hash.js --generate');
}
if (!process.env.SESSION_SECRET) {
fatal.push('SESSION_SECRET must be set in production (min 32 chars)');
} else if (process.env.SESSION_SECRET.length < 32) {
fatal.push('SESSION_SECRET must be at least 32 characters in production');
}
if (fatal.length > 0) {
console.error('FATAL: Missing required environment configuration:');
fatal.forEach(msg => console.error(' -', msg));
process.exit(1);
}
} else {
if (!process.env.ADMIN_PASSWORD_HASH) {
console.warn('⚠️ ADMIN_PASSWORD_HASH not set. Admin login will fail until configured.');
}
if (!process.env.SESSION_SECRET) {
const crypto = require('crypto');
process.env.SESSION_SECRET = crypto.randomBytes(64).toString('hex');
console.warn('⚠️ Using auto-generated session secret (dev only). Set SESSION_SECRET in production!');
}
}
Validation
Code evidence
-
marketplace/backend/server.js:167–178 configures the session without store:.
-
The supplied validation record locates the admin privilege assignment at adminRoutes.js:121–123 and contrasts it with routes/auth.js regeneration calls.
Limitations
-
The deployed admin API returned 404, so session behavior was not runtime-tested.
Recommended remediation
Configure a persistent, production-appropriate session store and call req.session.regenerate() before assigning authenticated or administrative session state.
6. Replace localStorage Credential Storage with HttpOnly Session Cookies and Clear Storage on Logout
ID: SEC-006
Severity: High
Category: Cryptographic Failures
Affected code: marketplace/frontend/cart.js, marketplace/frontend/checkout.html, marketplace/frontend/brands/teneo/brand-manager.html, marketplace/frontend/manage-books.html, marketplace/frontend/admin/lulu-manager.html, marketplace/frontend/js/*.js
Impact
Any JavaScript executing in the site origin can read persisted admin credentials or bearer tokens. Logout does not remove these values, leaving them available after the apparent session ends.
Technical details
manage-books.html stores book_manager_auth in localStorage, and other frontend code reads it back.
- Multiple scripts read
teneo_token from localStorage and use it as a Bearer token; the supplied record states that the logout flow does not clear localStorage or sessionStorage.
Relevant code
marketplace/frontend/checkout.html:1-30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Checkout</title>
<link rel="stylesheet" href="styles/variables.css">
<link rel="stylesheet" href="styles/base.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: #F9FAFB;
color: #111827;
line-height: 1.6;
min-height: 100vh;
}
header {
background: rgba(255,255,255,0.95);
border-bottom: 1px solid #E5E7EB;
padding: 1.25rem 0;
backdrop-filter: blur(10px);
}
.header-inner {
max-width: 900px;
margin: 0 auto;
padding: 0 24px;
Validation
Code evidence
-
The supplied validation record locates localStorage.setItem('book_manager_auth', authToken) in manage-books.html and reads in lulu-manager.html.
-
It also identifies Bearer-token reads in book-performance-modal.js, rewards.js, and published-dashboard.js, plus logout code in auth.js that redirects without clearing storage.
Runtime evidence
-
The deployed manage-books.html page reportedly contained localStorage.getItem('book_manager_auth').
Limitations
-
The provided snippet is from checkout.html and does not include the cited storage operations.
Recommended remediation
Move authentication state to appropriately scoped, Secure, HttpOnly cookies and remove bearer credentials from localStorage. Clear remaining client-side authentication state during logout.
7. Replace document.write of URL Parameter with Safe DOM Insertion
ID: SEC-007
Severity: High
Category: Injection
Affected code: marketplace/frontend/brands/master-templates/purchase-success.html
Impact
A crafted amount query parameter can inject HTML and script into the purchase-success page, causing reflected client-side XSS when the page is opened.
Technical details
- The page reads
amount with URLSearchParams and writes it through document.write() without validation or encoding.
- A value containing markup can close the surrounding element and introduce additional HTML, including script elements.
Relevant code
marketplace/frontend/brands/master-templates/purchase-success.html:1-30
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Purchase Complete - {{BRAND_NAME}}</title>
<meta name="description" content="Thank you for your purchase. Your order is being processed.">
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- Design System Base -->
<link rel="stylesheet" href="../../styles/variables.css">
<link rel="stylesheet" href="../../styles/base.css">
<!-- Custom Styles -->
<style>
:root {
--primary-color: {{PRIMARY_COLOR|#1E40AF}};
--accent-color: {{ACCENT_COLOR|#F59E0B}};
--success-color: #10B981;
}
.success-animation {
animation: successPulse 0.6s ease-out;
}
@keyframes successPulse {
Validation
Code evidence
-
The supplied validation record locates document.write(new URLSearchParams(window.location.search).get('amount') || '19.99') at line 125 of marketplace/frontend/brands/master-templates/purchase-success.html.
Runtime evidence
-
The supplied record reports that the deployed page returned HTTP 200 and contained the vulnerable document.write call.
Limitations
-
The supplied runtime observation confirms the vulnerable code was served, not that a script payload executed.
Recommended remediation
Replace document.write() with a specific DOM element assignment using textContent, and validate amount as the expected numeric format before displaying it.
8. Replace innerHTML Assignments with Safe DOM APIs in Frontend Renderers
ID: SEC-008
Severity: High
Category: Injection
Affected code: marketplace/frontend/js/product-page-enhanced.js:386, marketplace/frontend/js/success-stories.js:139,144,152,212, marketplace/frontend/brands/master-templates/main.js:368, marketplace/frontend/js/publisher-profile.js:106, funnel-module/frontend/js/course-player.js:339,347, marketplace/frontend/js/success-stories.js:144, 152, 212, publisher-profile.js:106, course-player.js:339,347, success-stories.js:139,144,152
Impact
Untrusted API-sourced or stored profile and content fields can execute JavaScript in visitors' browsers when rendered through unsafe innerHTML assignments.
Technical details
publisher-profile.js inserts profile.profile_image_url and displayName into an HTML template without escaping or URL validation.
- The supplied record identifies similar unescaped interpolations for success-story and tips fields; it excludes one static
product-page-enhanced.js occurrence from the confirmed data flow.
Relevant code
marketplace/frontend/js/publisher-profile.js:1-30
class PublisherProfile {
constructor() {
this.userId = this.getUserIdFromUrl();
this.currentUser = null;
this.profile = null;
this.badges = {
'bronze_book': { icon: '📘', name: 'Bronze Book', color: '#cd7f32' },
'silver_stack': { icon: '📚', name: 'Silver Stack', color: '#c0c0c0' },
'gold_trophy': { icon: '🏆', name: 'Gold Trophy', color: '#ffd700' },
'diamond': { icon: '💎', name: 'Diamond', color: '#b9f2ff' },
'crown': { icon: '👑', name: 'Crown', color: '#ff6b6b' },
'rocket': { icon: '🚀', name: 'Rocket', color: '#4ecdc4' },
'star': { icon: '🌟', name: 'Star', color: '#ffe66d' }
};
this.init();
}
getUserIdFromUrl() {
const pathParts = window.location.pathname.split('/');
const profileIndex = pathParts.indexOf('profile');
return profileIndex !== -1 && pathParts[profileIndex + 1] ? pathParts[profileIndex + 1] : null;
}
async init() {
if (!this.userId) {
this.showError('Invalid profile URL');
return;
}
Validation
Code evidence
-
The supplied validation record cites publisher-profile.js:106: avatarContainer.innerHTML = \
``.
-
It also cites success-stories.js around lines 124, 128, 139, and 212 for API fields inserted into innerHTML.
Runtime evidence
-
The deployed site reportedly returned HTTP 200.
Limitations
-
The backend API returned 404 in the supplied validation record, so the profile-update-to-execution chain was not runtime-tested.
-
No exploit execution was demonstrated.
Recommended remediation
Use textContent for text fields, validate URL schemes and destinations before assigning URL attributes, and sanitize any intentionally supported markup with an allowlist.
9. Escape Book Title and Description Before innerHTML Assignment in Marketplace Grid
ID: SEC-009
Severity: High
Category: Injection
Affected code: marketplace/frontend/js/marketplace.js
Impact
Poisoned catalog fields can execute JavaScript for marketplace visitors when catalog cards are rendered.
Technical details
createBookCard() interpolates book.title, description, author, badge, and other fields into a template assigned to grid.innerHTML without escaping.
- The supplied record states that authenticated admin book-management code writes caller-supplied book data to the catalog, which is then served to users.
Relevant code
marketplace/frontend/js/marketplace.js:145-159
<div class="book-card" data-book-id="${book.id}">
<a href="/book-detail.html?id=${book.id}&brand=${currentBrand}" class="book-link">
<img src="${book.coverImage}" alt="${book.title}" class="book-cover"
onerror="this.src='https://via.placeholder.com/400x600?text=${encodeURIComponent(book.title)}'">
<div class="book-info">
${book.badge ? `<span class="book-badge">${book.badge}</span>` : ''}
<h3 class="book-title">${book.title}</h3>
<p class="book-author">${book.author}</p>
<p class="book-description">${book.description}</p>
<div class="book-price">
<span class="current-price">$${book.price}</span>
${book.originalPrice ? `<span class="original-price">$${book.originalPrice}</span>` : ''}
</div>
</div>
</a>
Validation
Code evidence
-
marketplace/frontend/js/marketplace.js:145–159 places fields such as ${book.title}, ${book.author}, and ${book.description} inside HTML assigned to the grid.
Runtime evidence
-
The deployed root page reportedly contains the active books-grid element used by client-side catalog rendering.
Limitations
-
The supplied evidence does not include a live poisoned catalog or script-execution result.
Recommended remediation
Build cards with DOM APIs and assign untrusted text through textContent. Validate URL attributes separately, and sanitize any intentionally supported rich text with an allowlist.
10. Escape Dynamic Fields in Server-Side Storefront Template Renderer
ID: SEC-010
Severity: High
Category: Injection
Affected code: marketplace/backend/services/storeRendererService.js, marketplace/backend/server.js
Impact
Merchant-controlled store and product values can become stored XSS in storefront responses and affect storefront visitors.
Technical details
fillTemplate converts values with String(val) without HTML encoding.
- The supplied record identifies raw interpolations of store name, tagline, product name, description, and ID in renderer fallbacks; stored HTML is served by
/store/:slug without sanitization.
Relevant code
marketplace/backend/services/storeRendererService.js:1-30
/**
* Store Renderer Service
*
* Converts a store_config JSON (produced by aiStoreBuilderService) into a complete HTML page
* using the component library in marketplace/frontend/components-library/.
*
* Usage:
* const { renderStorePage } = require('./storeRendererService');
* const html = renderStorePage(storeConfig);
*
* Works without ANTHROPIC_API_KEY — pure template rendering, no AI calls.
*/
const fs = require('fs');
const path = require('path');
const COMPONENTS_DIR = path.join(__dirname, '../../frontend/components-library');
/**
* Load a component HTML file. Returns empty string if the file is a stub or missing.
*/
function loadComponent(relPath) {
const fullPath = path.join(COMPONENTS_DIR, relPath);
if (!fs.existsSync(fullPath)) return '';
const content = fs.readFileSync(fullPath, 'utf8').trim();
// Stub components are just a comment line (e.g. "<!-- Book Card (individual) -->")
if (content.length < 100) return '';
return content;
}
Validation
Code evidence
-
marketplace/backend/services/storeRendererService.js contains the renderer described above; the supplied validation record locates fillTemplate around lines 35–39 and additional raw interpolations around lines 105–107, 129–131, and 252–254.
-
The record states that /store/:slug sends stored HTML with res.send(store.html).
Limitations
-
The deployed store-builder and store routes returned 404, so the stored-XSS path was not runtime-tested.
Recommended remediation
Apply context-aware HTML escaping to all dynamic template values, or sanitize explicitly supported rich text with a server-side allowlist before storage and response.
11. Register Stripe Webhook Route Before Global Body-Parser to Prevent Signature Bypass
ID: SEC-011
Severity: High
Category: Insecure Design
Affected code: marketplace/backend/routes/checkout.js, marketplace/backend/server.js, server.js
Impact
If the global JSON parser consumes the webhook body before the Stripe route's raw-body middleware, signature verification may fail or behave incorrectly, creating a potential forged-webhook risk.
Technical details
- The supplied record states that
bodyParser.json() is mounted globally before checkout routes.
- The checkout webhook then calls
stripe.webhooks.constructEvent(req.body, sig, webhookSecret); the supplied evidence says req.body may already be a parsed object rather than the original raw bytes.
Relevant code
marketplace/backend/routes/checkout.js:1-30
const express = require('express');
const router = express.Router();
const crypto = require('crypto');
const rateLimit = require('express-rate-limit');
const { safeMessage, sanitizeMetadataValue, isValidEmail } = require('../utils/validate');
const path = require('path');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const btcpayService = require('../services/btcpayService');
const arxmintService = require('../services/arxmintService');
const stripeHealthService = require('../services/stripeHealthService');
const {
sanitizeBrandId,
lookupBookPrice,
applyCouponToPrice,
getNextReadOffer,
resolveCatalogItem
} = require('../services/checkoutOfferService');
const couponService = require('../services/couponService');
const orderBumpService = require('../services/orderBumpService');
const OrderService = require('../services/orderService');
const orderService = new OrderService();
const emailService = require('../services/emailService');
const { processMixedOrder } = require('./checkoutMixed');
const nftService = require('../services/nftService');
const shippingService = require('../services/shippingService');
const db = require('../database/database');
const { enrollUserInCourse } = require('./courseRoutes');
const licenseKeyService = require('../services/licenseKeyService');
const { trackReferral } = require('./referralRoutes');
Validation
Code evidence
-
The supplied validation record locates global bodyParser.json() at server.js:199, checkout route mounting at line 256, and per-route express.raw() near checkout.js:298.
Limitations
-
The deployed webhook endpoint returned 404, so signature behavior was not runtime-tested.
-
The record describes a potential failure or bypass on affected stripe-node versions; no forged webhook was demonstrated.
Recommended remediation
Ensure the webhook route receives the untouched request bytes before any global JSON parser runs, and verify the Stripe signature over that raw body.
12. Scope Order Lookup to Authenticated User to Prevent IDOR
ID: SEC-012
Severity: High
Category: Broken Access Control
Affected code: marketplace/backend/routes/checkout.js
Impact
The order-status endpoint can disclose order details to callers who know or enumerate an order ID because the lookup is not scoped to an authenticated owner.
Technical details
GET /order/:orderId calls orderService.getOrder(orderId) without authentication middleware shown in the supplied route.
- The supplied record states that the service query filters only by
order_id and not by user_id.
Relevant code
marketplace/backend/routes/checkout.js:684-696
}
}
}
}
// Get order status
router.get('/order/:orderId', async (req, res) => {
try {
const { orderId } = req.params;
const order = await orderService.getOrder(orderId);
if (!order) {
return res.status(404).json({ error: 'Order not found' });
Validation
Code evidence
-
marketplace/backend/routes/checkout.js:690 defines the order route and calls getOrder(orderId).
-
The supplied validation record identifies the underlying query as SELECT * FROM orders WHERE order_id = ?.
Limitations
-
The deployed order endpoint returned 404, so unauthorized retrieval was not runtime-tested.
-
The record contains conflicting descriptions of authentication; the supplied route evidence supports that no authentication middleware is shown.
Recommended remediation
Require authentication and add an ownership predicate such as the authenticated user's identifier to the order query. Return the same not-found response when the order is absent or not owned by the caller.
13. Validate Redirect URL Parameter in Email Marketing Endpoint to Prevent Open Redirect
ID: SEC-013
Severity: Medium
Category: Broken Access Control
Affected code: marketplace/backend/routes/emailMarketing.js:412-434, marketplace/backend/routes/emailMarketing.js:412–434
Impact
An unauthenticated attacker can use the click-tracking endpoint to redirect visitors to an arbitrary external destination, supporting phishing and brand impersonation.
Technical details
GET /track/click/:sendId assigns req.query.url to destination and passes it directly to res.redirect(302, destination).
- The handler performs no URL parsing, protocol validation, hostname validation, or allowlist check.
Relevant code
marketplace/backend/routes/emailMarketing.js:406-440
// Non-fatal — pixel already sent
}
});
// GET /api/email-marketing/track/click/:sendId?url=https://...
// Logs a click then redirects to the destination URL
router.get('/track/click/:sendId', async (req, res) => {
const destination = req.query.url;
const sendId = parseInt(req.params.sendId, 10);
// Record asynchronously then redirect
if (Number.isFinite(sendId) && destination) {
try {
await dbRun(
`UPDATE email_sends SET clicked_at = COALESCE(clicked_at, CURRENT_TIMESTAMP) WHERE id = ?`,
[sendId]
);
await dbRun(
`INSERT INTO email_events (event_type, send_id, url, ip_address, user_agent)
VALUES ('click', ?, ?, ?, ?)`,
[sendId, destination, req.ip || null, req.get('user-agent') || null]
);
} catch (e) {
// Non-fatal
}
}
if (destination) {
res.redirect(302, destination);
} else {
res.status(400).send('Missing url parameter');
}
});
// ─── Unsubscribe (public) ─────────────────────────────────────────────────────
Validation
Code evidence
-
marketplace/backend/routes/emailMarketing.js:412–434: const destination = req.query.url; when present, it is passed to res.redirect(302, destination).
-
The supplied record identifies emailTracking.js:131–143 as an in-repository implementation that parses and validates redirect URLs.
Runtime evidence
-
The route is mounted at /api/email-marketing/track/click/:sendId according to the supplied validation record.
Limitations
-
The deployed URL returned 404 for the route, so live redirect behavior was not confirmed.
Recommended remediation
Parse the value with URL, permit only the intended protocols and destinations, and reject invalid or disallowed targets before calling res.redirect.
14. HTML-Encode Subscriber Email Before Embedding in Server-Rendered Unsubscribe Response
ID: SEC-014
Severity: Medium
Category: Injection
Affected code: marketplace/backend/routes/emailMarketing.js:458-462, marketplace/backend/routes/emailTracking.js, marketplace/backend/routes/emailMarketing.js:458, 461
Impact
A malicious subscriber email can be stored and later reflected as HTML in the unsubscribe response, enabling XSS for anyone opening the confirmation page.
Technical details
- The supplied record states that the unsubscribe handler interpolates
subscriber.email directly into an HTML response at emailMarketing.js:461.
- The subscription validation regex accepts HTML metacharacters, allowing malicious content to reach storage after trimming and lowercasing.
Relevant code
marketplace/backend/routes/emailMarketing.js:1-30
/**
* Email Marketing Management Routes
*
* All operations use the shared DB adapter (SQLite local / Supabase in production).
*
* Subscriber Management (admin):
* GET /api/email-marketing/subscribers - list subscribers with pagination
* GET /api/email-marketing/subscribers/:id - get subscriber details
* DELETE /api/email-marketing/subscribers/:id - remove subscriber
*
* Email Sequences (admin):
* GET /api/email-marketing/sequences - list sequences
* POST /api/email-marketing/sequences - create sequence
* PUT /api/email-marketing/sequences/:id - update sequence
* DELETE /api/email-marketing/sequences/:id - delete sequence
* GET /api/email-marketing/sequences/:id/emails - list sequence emails
* POST /api/email-marketing/sequences/:id/emails - add email to sequence
*
* Broadcasts (admin):
* GET /api/email-marketing/broadcasts - list broadcasts
* POST /api/email-marketing/broadcasts - create broadcast
*
* Unsubscribe (public):
* GET /api/email-marketing/unsubscribe/:token - unsubscribe via token link
*/
const express = require('express');
const router = express.Router();
const { authenticateAdmin } = require('../middleware/auth');
const { dbRun, dbGet, dbAll } = require('../services/databaseHelper');
Validation
Code evidence
-
The supplied validation record cites emailTracking.js:30 regex /^[^\s@]+@[^\s@]+\.[^\s@]+$/ as accepting HTML metacharacters.
-
It cites marketplace/backend/routes/emailMarketing.js:461 as interpolating ${subscriber.email} into res.send(...) without encoding.
Limitations
-
The deployed unsubscribe endpoint returned 404, so the stored-and-reflected execution chain was not runtime-tested.
Recommended remediation
Use strict email validation, HTML-encode the email before embedding it in the response, and preferably construct the confirmation response with a safe templating mechanism.
15. Remove Logout Route from CSRF Exclusion List
ID: SEC-015
Severity: Medium
Category: Broken Access Control
Affected code: marketplace/backend/server.js, marketplace/backend/routes/auth.js
Impact
A cross-site request can terminate a user's authenticated session because logout is excluded from the application's CSRF protection.
Technical details
/api/auth/logout is listed in csrfExcludePaths.
- The logout handler destroys the session, while the supplied record states that the authentication check does not provide Origin, Referer, or CSRF-token validation.
Relevant code
marketplace/backend/server.js:1-30
// marketplace/backend/server.js
require('dotenv').config();
// Environment variable validation
function validateEnvironment() {
if (process.env.NODE_ENV === 'production') {
const fatal = [];
if (!process.env.ADMIN_PASSWORD_HASH) {
fatal.push('ADMIN_PASSWORD_HASH must be set in production. Run: node scripts/generate-password-hash.js --generate');
}
if (!process.env.SESSION_SECRET) {
fatal.push('SESSION_SECRET must be set in production (min 32 chars)');
} else if (process.env.SESSION_SECRET.length < 32) {
fatal.push('SESSION_SECRET must be at least 32 characters in production');
}
if (fatal.length > 0) {
console.error('FATAL: Missing required environment configuration:');
fatal.forEach(msg => console.error(' -', msg));
process.exit(1);
}
} else {
if (!process.env.ADMIN_PASSWORD_HASH) {
console.warn('⚠️ ADMIN_PASSWORD_HASH not set. Admin login will fail until configured.');
}
if (!process.env.SESSION_SECRET) {
const crypto = require('crypto');
process.env.SESSION_SECRET = crypto.randomBytes(64).toString('hex');
console.warn('⚠️ Using auto-generated session secret (dev only). Set SESSION_SECRET in production!');
}
}
Validation
Code evidence
-
marketplace/backend/server.js:215 excludes /api/auth/logout from CSRF protection.
-
The supplied validation record cites auth.js:445–452 for the authentication check and states that POST /logout calls req.session.destroy().
Limitations
-
The deployed logout endpoint returned 404, so forced logout was not runtime-tested.
Recommended remediation
Remove logout from the CSRF exclusion list and protect the state-changing request with the application's CSRF token mechanism or strict Origin/Referer validation.
16. Validate returnUrl Parameter Before Assigning to window.location in Funnel Builder
ID: SEC-016
Severity: Medium
Category: Broken Access Control
Affected code: funnel-module/frontend/js/funnel-builder.js
Impact
When the funnel module is served, a crafted returnUrl can redirect users to an arbitrary external destination; the supplied record also identifies acceptance of javascript: values.
Technical details
parseURLContext() stores the query parameter returnUrl without validation.
window.location.href is later assigned this.context.returnUrl without checking protocol or origin.
Relevant code
funnel-module/frontend/js/funnel-builder.js:1-30
// Funnel Builder - Core Logic
// Integrates with template-processor.js for variable replacement
class FunnelBuilder {
constructor() {
this.selectedTemplate = null;
this.variables = {};
this.context = null;
this.autoSaveInterval = null;
this.templateProcessor = null;
this.aiPrompts = null;
this.init();
}
async init() {
console.log('Initializing Funnel Builder...');
// Initialize template processor
if (typeof TemplateProcessor !== 'undefined') {
this.templateProcessor = new TemplateProcessor();
}
// Load AI prompts
await this.loadAIPrompts();
// Parse URL context (course integration)
this.context = this.parseURLContext();
// Setup UI
Validation
Code evidence
-
The supplied validation record cites funnel-module/frontend/js/funnel-builder.js:71 for raw returnUrl capture and line 911 for assignment to window.location.href.
Limitations
-
The funnel module is not included in the supplied deployment configuration and the deployed page returned 404; only static code evidence is available.
Recommended remediation
Parse the value with new URL(value, location.origin), require the intended protocol and origin, and reject external or otherwise invalid destinations before navigation.
17. Enable TLS Certificate Validation for PostgreSQL Connections
ID: SEC-017
Severity: Medium
Category: Cryptographic Failures
Affected code: marketplace/backend/database/database.js:212, marketplace/backend/database/init.js:126
Impact
When PostgreSQL TLS is enabled, the client accepts any server certificate, allowing a man-in-the-middle to impersonate the database endpoint and intercept or alter database traffic.
Technical details
database.js enables SSL when PGSSLMODE=require, PGSSL=true, or the connection string matches supabase.co.
- The resulting configuration sets
rejectUnauthorized: false, disabling certificate verification.
Relevant code
marketplace/backend/database/database.js:206-218
const databaseUrl = process.env.DATABASE_URL || process.env.SUPABASE_DB_URL;
const sslRequested = process.env.PGSSLMODE === 'require' || process.env.PGSSL === 'true' || /supabase\.co/i.test(databaseUrl || '');
const pool = new Pool({
connectionString: databaseUrl,
ssl: sslRequested ? { rejectUnauthorized: false } : undefined,
});
pool.on('error', (err) => {
console.error('[DB] Unexpected PostgreSQL pool error:', err.message);
});
Validation
Code evidence
-
marketplace/backend/database/database.js:206–218 constructs the pool with ssl: sslRequested ? { rejectUnauthorized: false } : undefined.
-
The supplied finding also identifies the same setting in marketplace/backend/database/init.js:126.
Runtime evidence
-
The deployed application reportedly responded to an HTTP health URL, but this did not test database certificate validation.
Limitations
-
No database connection or man-in-the-middle test was performed.
Recommended remediation
Enable certificate verification for PostgreSQL connections and configure the required trusted CA material rather than disabling rejectUnauthorized.
18. Sanitize Funnel Template HTML Before Writing to Preview Window via document.write
ID: SEC-018
Severity: Medium
Category: Injection
Affected code: funnel-module/frontend/js/funnel-builder.js:934
Impact
Funnel template content containing attacker-controlled markup or scripts is written into a new browser window and can execute in the preview context.
Technical details
fullscreenPreview() obtains processed template HTML and passes it directly to win.document.write(processedHTML).
- The supplied record states that variable values originate from form input and are substituted into the template without sanitization before this write.
Relevant code
funnel-module/frontend/js/funnel-builder.js:928-940
document.getElementById(`preview-${mode}`)?.classList.add('active');
}
fullscreenPreview() {
const processedHTML = this.getProcessedHTML();
const win = window.open('', '_blank');
win.document.write(processedHTML);
win.document.close();
}
showNotification(message, type = 'success') {
const toast = document.getElementById('notification-toast');
const icon = document.getElementById('notification-icon');
Validation
Code evidence
-
funnel-module/frontend/js/funnel-builder.js:934 calls win.document.write(processedHTML) and then closes the document.
Limitations
-
The funnel module returned 404 on the deployed URL, so no live payload execution was observed.
-
The supplied record characterizes the demonstrated path as self-XSS unless drafts are shared.
Recommended remediation
Do not write untrusted HTML directly with document.write(). Sanitize permitted template content with a strict allowlist before previewing, or render through safe DOM APIs and isolate the preview context.
19. Reject Array-Valued Query Parameters to Prevent HTTP Parameter Pollution DoS and Injection
ID: SEC-019
Severity: Medium
Category: Mishandling of Exceptional Conditions
Affected code: marketplace/backend/routes/storefront.js, marketplace/backend/routes/merchantFulfillment.js, marketplace/backend/routes/adminRoutes.js, routes/storefront.js, routes/merchantFulfillment.js, routes/adminRoutes.js
Impact
Duplicate query parameters can trigger unhandled type errors on public routes and produce incorrect path or query handling on other routes, enabling request-level denial of service or logic errors.
Technical details
- Express can represent duplicate query keys as arrays, but the supplied handlers use values without type checks.
storefront.js calls req.query.category.toLowerCase(), while the supplied record identifies an unvalidated brand_id passed to path.join() in adminRoutes.js.
Relevant code
marketplace/backend/routes/storefront.js:1-30
/**
* Storefront API — Standardized catalog and fulfillment endpoints.
*
* This is the API that external consumers (like ArxMint's /bazaar page) use
* to fetch product catalogs and trigger order fulfillment.
*
* Endpoints:
* GET /api/storefront/catalog - Full product catalog (standardized schema)
* GET /api/storefront/catalog/:category - Products filtered by category
* GET /api/storefront/product/:id - Single product detail
* POST /api/storefront/checkout - Create checkout session (routes to payment provider)
* POST /api/storefront/fulfill - Webhook: payment confirmed → fulfill order
*
* Schema matches BAZAAR_STRATEGY.md Product interface.
*/
'use strict';
const express = require('express');
const router = express.Router();
const fs = require('fs').promises;
const path = require('path');
const crypto = require('crypto');
const axios = require('axios');
const arxmintProvider = require('../services/arxmintProvider');
const OrderService = require('../services/orderService');
const emailService = require('../services/emailService');
const fulfillmentService = require('../services/fulfillmentService');
const zapService = require('../services/zapService');
Validation
Code evidence
-
The supplied validation record locates the category call in marketplace/backend/routes/storefront.js:276.
-
It locates the path.join(..., brand, ...) use in adminRoutes.js:933–934 and states that duplicate brand_id values become arrays.
Limitations
-
The deployed API returned 404, so duplicate-parameter behavior was not runtime-tested.
-
The supplied record identifies multiple affected files but provides concrete line-level behavior only for the cited storefront and admin paths.
Recommended remediation
Validate that each query parameter is a single string before string, path, or database operations. Reject arrays and duplicate values with a client error, and handle validation failures without uncaught exceptions.
Security report for Traviseric/teneo-marketplace
Hello maintainers,
I am a security researcher studying security risks in vibe-coded software. During this research, I reviewed this repository and identified the findings below. These findings were identified in commit
1dee886. Each finding has undergone human analysis, but the report may still contain mistakes or incomplete interpretations. Please review this report and apply the necessary security fixes.This report contains 19 confirmed findings identified during security review. Validation details below distinguish code evidence from runtime observations and note any limitations.
Summary
marketplace/frontend/brands/teneo/config.json:7marketplace/backend/config/network.js:49marketplace/backend/middleware/teneoAuth.js:112-119marketplace/backend/middleware/teneoAuth.js:125-137marketplace/backend/middleware/teneoAuth.js:112-119,125-137marketplace/frontend/admin-dashboard.htmlmarketplace/backend/routes/storeBuilder.jsmarketplace/backend/server.jsmarketplace/backend/routes/adminRoutes.jsmarketplace/frontend/cart.jsmarketplace/frontend/checkout.htmlmarketplace/frontend/brands/teneo/brand-manager.htmlmarketplace/frontend/manage-books.htmlmarketplace/frontend/admin/lulu-manager.htmlmarketplace/frontend/js/*.jsmarketplace/frontend/brands/master-templates/purchase-success.htmlmarketplace/frontend/js/product-page-enhanced.js:386marketplace/frontend/js/success-stories.js:139,144,152,212marketplace/frontend/brands/master-templates/main.js:368marketplace/frontend/js/publisher-profile.js:106funnel-module/frontend/js/course-player.js:339,347marketplace/frontend/js/success-stories.js:144, 152, 212publisher-profile.js:106course-player.js:339,347success-stories.js:139,144,152marketplace/frontend/js/marketplace.jsmarketplace/backend/services/storeRendererService.jsmarketplace/backend/server.jsmarketplace/backend/routes/checkout.jsmarketplace/backend/server.jsserver.jsmarketplace/backend/routes/checkout.jsmarketplace/backend/routes/emailMarketing.js:412-434marketplace/backend/routes/emailMarketing.js:412–434marketplace/backend/routes/emailMarketing.js:458-462marketplace/backend/routes/emailTracking.jsmarketplace/backend/routes/emailMarketing.js:458, 461marketplace/backend/server.jsmarketplace/backend/routes/auth.jsfunnel-module/frontend/js/funnel-builder.jsmarketplace/backend/database/database.js:212marketplace/backend/database/init.js:126funnel-module/frontend/js/funnel-builder.js:934marketplace/backend/routes/storefront.jsmarketplace/backend/routes/merchantFulfillment.jsmarketplace/backend/routes/adminRoutes.jsroutes/storefront.jsroutes/merchantFulfillment.jsroutes/adminRoutes.js1. Remove Hardcoded RSA Private Key from Source Control
ID:
SEC-001Severity: Critical
Category: Cryptographic Failures
Affected code:
marketplace/frontend/brands/teneo/config.json:7,marketplace/backend/config/network.js:49Impact
The RSA private key is exposed in a public, git-tracked configuration and can be used to forge federation signatures. Existing copies remain compromised even after removing the current file.
Technical details
private_keyvalue, and the supplied validation record identifies it as a full PKCS8 PEM RSA-2048 private key.marketplace/backend/config/network.js:49assignsconfig.private_keytothis.privateKeyfor federation-message signing.Relevant code
marketplace/frontend/brands/teneo/config.json:1-10{ "network_enabled": false, "share_catalog": false, "accept_referrals": false, "referral_percentage": 0, "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsDBBNc1QuYFCesEXFvNq\nElu+/vm4xqJjItamekYKwYpA5llifPCS8rem4cj+DivUwXrFum8x6HcnlZgRrpcQ\nQ9M+XIFeDYwwsHYLgexpSL/OuxKB4bXrbWI7da+vDOTYtH4RDN5lT3t7cheEIaHQ\nh3kOP/luF25xz5ZaNvLBBG/PTSIHRsFnvu1AObVhPhpxNCQej7t7WGsQGwCrr2OH\nT2QXovMH+DTmJo4q4RbbYyxyKr2iHHu4egAsdc5IXsKClTcBWMGQU5+0VNcX3sWG\n5dFKF0eqQNiLMXjOBPMPn8wUzyl5jNVbdto3yDgYjQYre4NBKhk8trJAs+k3FPW1\n6wIDAQAB\n-----END PUBLIC KEY-----\n", "private_key": "[REDACTED_SECRET]", "network_peers": [], "trusted_stores": [] }Validation
Code evidence
marketplace/frontend/brands/teneo/config.json:7contains theprivate_keyconfiguration field.marketplace/backend/config/network.js:49loads the configured private key directly.Limitations
The provided code snippet redacts the secret value, so the exact key material is not reproduced here.
No live probe was performed; the finding is based on repository evidence.
Recommended remediation
Remove the private key from the repository and purge it from git history. Revoke or rotate the exposed key, then load its replacement through runtime environment or secret-management injection.
2. Encrypt or Avoid Persisting Raw Access Tokens in Database
ID:
SEC-002Severity: High
Category: Cryptographic Failures
Affected code:
marketplace/backend/middleware/teneoAuth.js:112-119,marketplace/backend/middleware/teneoAuth.js:125-137,marketplace/backend/middleware/teneoAuth.js:112-119,125-137Impact
A database compromise exposes usable Teneo bearer tokens, allowing token replay against authenticated functionality.
Technical details
updateStoredTokeninserts the supplied token directly intoteneo_auth_tokens.access_token.getStoredTokenretrieves the token as plaintext; the supplied record identifies no hashing or encryption on this path.Relevant code
marketplace/backend/middleware/teneoAuth.js:106-125Validation
Code evidence
marketplace/backend/middleware/teneoAuth.js:112–119executesINSERT OR REPLACE ... VALUES (?, ?, ...)with[userId, token].The supplied validation record identifies
teneo_auth_tokens.access_tokenasTEXT NOT NULLand contrasts this with encrypted merchant credentials elsewhere.Limitations
The deployed
/api/auth/statusendpoint returned 404, so database behavior was not runtime-tested.Recommended remediation
Store a keyed digest or managed-key encryption of access tokens, compare or decrypt only when required, apply an appropriate token lifetime, and prevent token values from being logged.
3. Fix Stored XSS in Admin Dashboard Order Rendering
ID:
SEC-003Severity: High
Category: Injection
Affected code:
marketplace/frontend/admin-dashboard.htmlImpact
Attacker-controlled order data can execute JavaScript in an administrator's browser when the admin dashboard renders affected orders.
Technical details
renderOrders()interpolates database fields such ascustomer_email,book_title, andpayment_statusinto aninnerHTMLtemplate without escaping.customer_nameand other order fields originate from checkout-controlled data; anescapeHtml()helper exists in the file but is not applied to this renderer.Validation
Code evidence
The supplied validation record locates the vulnerable interpolation in
marketplace/frontend/admin-dashboard.htmlaround lines 2923–2943.The same record identifies an existing
escapeHtml()helper used by another renderer, whilerenderOrders()leaves the listed fields unescaped.Limitations
No code snippet was supplied for the dashboard renderer.
The deployed API was reported as unavailable, so the end-to-end injection path was not runtime-tested.
Recommended remediation
Render untrusted order fields with
textContentor equivalent safe DOM APIs. If HTML is required, apply context-appropriate allowlist sanitization before insertion.4. Escape User-Supplied Values in Store Builder HTML Template Renderer
ID:
SEC-004Severity: High
Category: Injection
Affected code:
marketplace/backend/routes/storeBuilder.jsImpact
Merchant-controlled storefront configuration can produce HTML containing script payloads, causing XSS for users who view the rendered or saved storefront.
Technical details
POST /api/store-builder/renderaccepts a caller-suppliedconfigand passes it torenderStorePagewithout an authentication gate shown in the supplied route.String(val)and raw template interpolation rather than HTML encoding values before constructing HTML.Relevant code
marketplace/backend/routes/storeBuilder.js:56-77Validation
Code evidence
marketplace/backend/routes/storeBuilder.js:62–71acceptsreq.body.configand returns the result ofrenderStorePage(config).The supplied validation record identifies
storeRendererService.js:35and inline interpolations around lines 178, 252, 254, and 268 as unescaped HTML sinks.Limitations
The deployed render endpoint returned 404, so exploitation was not confirmed against the deployed instance.
Recommended remediation
HTML-escape all configuration values before insertion, or use safe DOM/template APIs. Sanitize explicitly allowed rich text with a server-side allowlist, and require authorization for store creation or rendering where appropriate.
5. Configure Persistent Session Store to Replace Insecure MemoryStore Default
ID:
SEC-005Severity: High
Category: Authentication Failures
Affected code:
marketplace/backend/server.js,marketplace/backend/routes/adminRoutes.jsImpact
Sessions are held in process memory and disappear on restart. The admin login path also preserves the existing session identifier while granting admin state, creating a session-fixation risk.
Technical details
express-sessionis configured without astoreoption inserver.js, so the default MemoryStore is used.adminRoutes.js:121–123setsreq.session.isAdmin = truewithout callingreq.session.regenerate(); the supplied record identifies regeneration in several other authentication paths.Relevant code
marketplace/backend/server.js:1-30Validation
Code evidence
marketplace/backend/server.js:167–178configures the session withoutstore:.The supplied validation record locates the admin privilege assignment at
adminRoutes.js:121–123and contrasts it withroutes/auth.jsregeneration calls.Limitations
The deployed admin API returned 404, so session behavior was not runtime-tested.
Recommended remediation
Configure a persistent, production-appropriate session store and call
req.session.regenerate()before assigning authenticated or administrative session state.6. Replace localStorage Credential Storage with HttpOnly Session Cookies and Clear Storage on Logout
ID:
SEC-006Severity: High
Category: Cryptographic Failures
Affected code:
marketplace/frontend/cart.js,marketplace/frontend/checkout.html,marketplace/frontend/brands/teneo/brand-manager.html,marketplace/frontend/manage-books.html,marketplace/frontend/admin/lulu-manager.html,marketplace/frontend/js/*.jsImpact
Any JavaScript executing in the site origin can read persisted admin credentials or bearer tokens. Logout does not remove these values, leaving them available after the apparent session ends.
Technical details
manage-books.htmlstoresbook_manager_authin localStorage, and other frontend code reads it back.teneo_tokenfrom localStorage and use it as a Bearer token; the supplied record states that the logout flow does not clear localStorage or sessionStorage.Relevant code
marketplace/frontend/checkout.html:1-30Validation
Code evidence
The supplied validation record locates
localStorage.setItem('book_manager_auth', authToken)inmanage-books.htmland reads inlulu-manager.html.It also identifies Bearer-token reads in
book-performance-modal.js,rewards.js, andpublished-dashboard.js, plus logout code inauth.jsthat redirects without clearing storage.Runtime evidence
The deployed
manage-books.htmlpage reportedly containedlocalStorage.getItem('book_manager_auth').Limitations
The provided snippet is from
checkout.htmland does not include the cited storage operations.Recommended remediation
Move authentication state to appropriately scoped, Secure, HttpOnly cookies and remove bearer credentials from localStorage. Clear remaining client-side authentication state during logout.
7. Replace document.write of URL Parameter with Safe DOM Insertion
ID:
SEC-007Severity: High
Category: Injection
Affected code:
marketplace/frontend/brands/master-templates/purchase-success.htmlImpact
A crafted
amountquery parameter can inject HTML and script into the purchase-success page, causing reflected client-side XSS when the page is opened.Technical details
amountwithURLSearchParamsand writes it throughdocument.write()without validation or encoding.Relevant code
marketplace/frontend/brands/master-templates/purchase-success.html:1-30Validation
Code evidence
The supplied validation record locates
document.write(new URLSearchParams(window.location.search).get('amount') || '19.99')at line 125 ofmarketplace/frontend/brands/master-templates/purchase-success.html.Runtime evidence
The supplied record reports that the deployed page returned HTTP 200 and contained the vulnerable
document.writecall.Limitations
The supplied runtime observation confirms the vulnerable code was served, not that a script payload executed.
Recommended remediation
Replace
document.write()with a specific DOM element assignment usingtextContent, and validateamountas the expected numeric format before displaying it.8. Replace innerHTML Assignments with Safe DOM APIs in Frontend Renderers
ID:
SEC-008Severity: High
Category: Injection
Affected code:
marketplace/frontend/js/product-page-enhanced.js:386,marketplace/frontend/js/success-stories.js:139,144,152,212,marketplace/frontend/brands/master-templates/main.js:368,marketplace/frontend/js/publisher-profile.js:106,funnel-module/frontend/js/course-player.js:339,347,marketplace/frontend/js/success-stories.js:144, 152, 212,publisher-profile.js:106,course-player.js:339,347,success-stories.js:139,144,152Impact
Untrusted API-sourced or stored profile and content fields can execute JavaScript in visitors' browsers when rendered through unsafe
innerHTMLassignments.Technical details
publisher-profile.jsinsertsprofile.profile_image_urlanddisplayNameinto an HTML template without escaping or URL validation.product-page-enhanced.jsoccurrence from the confirmed data flow.Relevant code
marketplace/frontend/js/publisher-profile.js:1-30Validation
Code evidence
The supplied validation record cites
``.
publisher-profile.js:106:avatarContainer.innerHTML = \It also cites
success-stories.jsaround lines 124, 128, 139, and 212 for API fields inserted intoinnerHTML.Runtime evidence
The deployed site reportedly returned HTTP 200.
Limitations
The backend API returned 404 in the supplied validation record, so the profile-update-to-execution chain was not runtime-tested.
No exploit execution was demonstrated.
Recommended remediation
Use
textContentfor text fields, validate URL schemes and destinations before assigning URL attributes, and sanitize any intentionally supported markup with an allowlist.9. Escape Book Title and Description Before innerHTML Assignment in Marketplace Grid
ID:
SEC-009Severity: High
Category: Injection
Affected code:
marketplace/frontend/js/marketplace.jsImpact
Poisoned catalog fields can execute JavaScript for marketplace visitors when catalog cards are rendered.
Technical details
createBookCard()interpolatesbook.title,description,author,badge, and other fields into a template assigned togrid.innerHTMLwithout escaping.Relevant code
marketplace/frontend/js/marketplace.js:145-159Validation
Code evidence
marketplace/frontend/js/marketplace.js:145–159places fields such as${book.title},${book.author}, and${book.description}inside HTML assigned to the grid.Runtime evidence
The deployed root page reportedly contains the active
books-gridelement used by client-side catalog rendering.Limitations
The supplied evidence does not include a live poisoned catalog or script-execution result.
Recommended remediation
Build cards with DOM APIs and assign untrusted text through
textContent. Validate URL attributes separately, and sanitize any intentionally supported rich text with an allowlist.10. Escape Dynamic Fields in Server-Side Storefront Template Renderer
ID:
SEC-010Severity: High
Category: Injection
Affected code:
marketplace/backend/services/storeRendererService.js,marketplace/backend/server.jsImpact
Merchant-controlled store and product values can become stored XSS in storefront responses and affect storefront visitors.
Technical details
fillTemplateconverts values withString(val)without HTML encoding./store/:slugwithout sanitization.Relevant code
marketplace/backend/services/storeRendererService.js:1-30Validation
Code evidence
marketplace/backend/services/storeRendererService.jscontains the renderer described above; the supplied validation record locatesfillTemplatearound lines 35–39 and additional raw interpolations around lines 105–107, 129–131, and 252–254.The record states that
/store/:slugsends stored HTML withres.send(store.html).Limitations
The deployed store-builder and store routes returned 404, so the stored-XSS path was not runtime-tested.
Recommended remediation
Apply context-aware HTML escaping to all dynamic template values, or sanitize explicitly supported rich text with a server-side allowlist before storage and response.
11. Register Stripe Webhook Route Before Global Body-Parser to Prevent Signature Bypass
ID:
SEC-011Severity: High
Category: Insecure Design
Affected code:
marketplace/backend/routes/checkout.js,marketplace/backend/server.js,server.jsImpact
If the global JSON parser consumes the webhook body before the Stripe route's raw-body middleware, signature verification may fail or behave incorrectly, creating a potential forged-webhook risk.
Technical details
bodyParser.json()is mounted globally before checkout routes.stripe.webhooks.constructEvent(req.body, sig, webhookSecret); the supplied evidence saysreq.bodymay already be a parsed object rather than the original raw bytes.Relevant code
marketplace/backend/routes/checkout.js:1-30Validation
Code evidence
The supplied validation record locates global
bodyParser.json()atserver.js:199, checkout route mounting at line 256, and per-routeexpress.raw()nearcheckout.js:298.Limitations
The deployed webhook endpoint returned 404, so signature behavior was not runtime-tested.
The record describes a potential failure or bypass on affected stripe-node versions; no forged webhook was demonstrated.
Recommended remediation
Ensure the webhook route receives the untouched request bytes before any global JSON parser runs, and verify the Stripe signature over that raw body.
12. Scope Order Lookup to Authenticated User to Prevent IDOR
ID:
SEC-012Severity: High
Category: Broken Access Control
Affected code:
marketplace/backend/routes/checkout.jsImpact
The order-status endpoint can disclose order details to callers who know or enumerate an order ID because the lookup is not scoped to an authenticated owner.
Technical details
GET /order/:orderIdcallsorderService.getOrder(orderId)without authentication middleware shown in the supplied route.order_idand not byuser_id.Relevant code
marketplace/backend/routes/checkout.js:684-696Validation
Code evidence
marketplace/backend/routes/checkout.js:690defines the order route and callsgetOrder(orderId).The supplied validation record identifies the underlying query as
SELECT * FROM orders WHERE order_id = ?.Limitations
The deployed order endpoint returned 404, so unauthorized retrieval was not runtime-tested.
The record contains conflicting descriptions of authentication; the supplied route evidence supports that no authentication middleware is shown.
Recommended remediation
Require authentication and add an ownership predicate such as the authenticated user's identifier to the order query. Return the same not-found response when the order is absent or not owned by the caller.
13. Validate Redirect URL Parameter in Email Marketing Endpoint to Prevent Open Redirect
ID:
SEC-013Severity: Medium
Category: Broken Access Control
Affected code:
marketplace/backend/routes/emailMarketing.js:412-434,marketplace/backend/routes/emailMarketing.js:412–434Impact
An unauthenticated attacker can use the click-tracking endpoint to redirect visitors to an arbitrary external destination, supporting phishing and brand impersonation.
Technical details
GET /track/click/:sendIdassignsreq.query.urltodestinationand passes it directly tores.redirect(302, destination).Relevant code
marketplace/backend/routes/emailMarketing.js:406-440Validation
Code evidence
marketplace/backend/routes/emailMarketing.js:412–434:const destination = req.query.url; when present, it is passed tores.redirect(302, destination).The supplied record identifies
emailTracking.js:131–143as an in-repository implementation that parses and validates redirect URLs.Runtime evidence
The route is mounted at
/api/email-marketing/track/click/:sendIdaccording to the supplied validation record.Limitations
The deployed URL returned 404 for the route, so live redirect behavior was not confirmed.
Recommended remediation
Parse the value with
URL, permit only the intended protocols and destinations, and reject invalid or disallowed targets before callingres.redirect.14. HTML-Encode Subscriber Email Before Embedding in Server-Rendered Unsubscribe Response
ID:
SEC-014Severity: Medium
Category: Injection
Affected code:
marketplace/backend/routes/emailMarketing.js:458-462,marketplace/backend/routes/emailTracking.js,marketplace/backend/routes/emailMarketing.js:458, 461Impact
A malicious subscriber email can be stored and later reflected as HTML in the unsubscribe response, enabling XSS for anyone opening the confirmation page.
Technical details
subscriber.emaildirectly into an HTML response atemailMarketing.js:461.Relevant code
marketplace/backend/routes/emailMarketing.js:1-30Validation
Code evidence
The supplied validation record cites
emailTracking.js:30regex/^[^\s@]+@[^\s@]+\.[^\s@]+$/as accepting HTML metacharacters.It cites
marketplace/backend/routes/emailMarketing.js:461as interpolating${subscriber.email}intores.send(...)without encoding.Limitations
The deployed unsubscribe endpoint returned 404, so the stored-and-reflected execution chain was not runtime-tested.
Recommended remediation
Use strict email validation, HTML-encode the email before embedding it in the response, and preferably construct the confirmation response with a safe templating mechanism.
15. Remove Logout Route from CSRF Exclusion List
ID:
SEC-015Severity: Medium
Category: Broken Access Control
Affected code:
marketplace/backend/server.js,marketplace/backend/routes/auth.jsImpact
A cross-site request can terminate a user's authenticated session because logout is excluded from the application's CSRF protection.
Technical details
/api/auth/logoutis listed incsrfExcludePaths.Relevant code
marketplace/backend/server.js:1-30Validation
Code evidence
marketplace/backend/server.js:215excludes/api/auth/logoutfrom CSRF protection.The supplied validation record cites
auth.js:445–452for the authentication check and states thatPOST /logoutcallsreq.session.destroy().Limitations
The deployed logout endpoint returned 404, so forced logout was not runtime-tested.
Recommended remediation
Remove logout from the CSRF exclusion list and protect the state-changing request with the application's CSRF token mechanism or strict Origin/Referer validation.
16. Validate returnUrl Parameter Before Assigning to window.location in Funnel Builder
ID:
SEC-016Severity: Medium
Category: Broken Access Control
Affected code:
funnel-module/frontend/js/funnel-builder.jsImpact
When the funnel module is served, a crafted
returnUrlcan redirect users to an arbitrary external destination; the supplied record also identifies acceptance ofjavascript:values.Technical details
parseURLContext()stores the query parameterreturnUrlwithout validation.window.location.hrefis later assignedthis.context.returnUrlwithout checking protocol or origin.Relevant code
funnel-module/frontend/js/funnel-builder.js:1-30Validation
Code evidence
The supplied validation record cites
funnel-module/frontend/js/funnel-builder.js:71for rawreturnUrlcapture and line 911 for assignment towindow.location.href.Limitations
The funnel module is not included in the supplied deployment configuration and the deployed page returned 404; only static code evidence is available.
Recommended remediation
Parse the value with
new URL(value, location.origin), require the intended protocol and origin, and reject external or otherwise invalid destinations before navigation.17. Enable TLS Certificate Validation for PostgreSQL Connections
ID:
SEC-017Severity: Medium
Category: Cryptographic Failures
Affected code:
marketplace/backend/database/database.js:212,marketplace/backend/database/init.js:126Impact
When PostgreSQL TLS is enabled, the client accepts any server certificate, allowing a man-in-the-middle to impersonate the database endpoint and intercept or alter database traffic.
Technical details
database.jsenables SSL whenPGSSLMODE=require,PGSSL=true, or the connection string matchessupabase.co.rejectUnauthorized: false, disabling certificate verification.Relevant code
marketplace/backend/database/database.js:206-218Validation
Code evidence
marketplace/backend/database/database.js:206–218constructs the pool withssl: sslRequested ? { rejectUnauthorized: false } : undefined.The supplied finding also identifies the same setting in
marketplace/backend/database/init.js:126.Runtime evidence
The deployed application reportedly responded to an HTTP health URL, but this did not test database certificate validation.
Limitations
No database connection or man-in-the-middle test was performed.
Recommended remediation
Enable certificate verification for PostgreSQL connections and configure the required trusted CA material rather than disabling
rejectUnauthorized.18. Sanitize Funnel Template HTML Before Writing to Preview Window via document.write
ID:
SEC-018Severity: Medium
Category: Injection
Affected code:
funnel-module/frontend/js/funnel-builder.js:934Impact
Funnel template content containing attacker-controlled markup or scripts is written into a new browser window and can execute in the preview context.
Technical details
fullscreenPreview()obtains processed template HTML and passes it directly towin.document.write(processedHTML).Relevant code
funnel-module/frontend/js/funnel-builder.js:928-940Validation
Code evidence
funnel-module/frontend/js/funnel-builder.js:934callswin.document.write(processedHTML)and then closes the document.Limitations
The funnel module returned 404 on the deployed URL, so no live payload execution was observed.
The supplied record characterizes the demonstrated path as self-XSS unless drafts are shared.
Recommended remediation
Do not write untrusted HTML directly with
document.write(). Sanitize permitted template content with a strict allowlist before previewing, or render through safe DOM APIs and isolate the preview context.19. Reject Array-Valued Query Parameters to Prevent HTTP Parameter Pollution DoS and Injection
ID:
SEC-019Severity: Medium
Category: Mishandling of Exceptional Conditions
Affected code:
marketplace/backend/routes/storefront.js,marketplace/backend/routes/merchantFulfillment.js,marketplace/backend/routes/adminRoutes.js,routes/storefront.js,routes/merchantFulfillment.js,routes/adminRoutes.jsImpact
Duplicate query parameters can trigger unhandled type errors on public routes and produce incorrect path or query handling on other routes, enabling request-level denial of service or logic errors.
Technical details
storefront.jscallsreq.query.category.toLowerCase(), while the supplied record identifies an unvalidatedbrand_idpassed topath.join()inadminRoutes.js.Relevant code
marketplace/backend/routes/storefront.js:1-30Validation
Code evidence
The supplied validation record locates the category call in
marketplace/backend/routes/storefront.js:276.It locates the
path.join(..., brand, ...)use inadminRoutes.js:933–934and states that duplicatebrand_idvalues become arrays.Limitations
The deployed API returned 404, so duplicate-parameter behavior was not runtime-tested.
The supplied record identifies multiple affected files but provides concrete line-level behavior only for the cited storefront and admin paths.
Recommended remediation
Validate that each query parameter is a single string before string, path, or database operations. Reject arrays and duplicate values with a client error, and handle validation failures without uncaught exceptions.