Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 138 additions & 123 deletions test/proxy-mock-api/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,147 +12,162 @@
const PROXY_DATABASE = process.env.PROXY_CLIENT_DATABASE || 'postgres';

function deriveCompanyId(username) {
// Single-username case (e.g. plain "proxy_user") keeps the legacy id so existing
// tests/clients that don't randomize still hit a stable companyId.
if (username === PROXY_USERNAME_PREFIX) {
return 'test-company-001';
}
const hash = crypto.createHash('sha1').update(username).digest('hex').slice(0, 12);
return `test-company-${hash}`;
// Single-username case (e.g. plain "proxy_user") keeps the legacy id so existing
// tests/clients that don't randomize still hit a stable companyId.
if (username === PROXY_USERNAME_PREFIX) {
return 'test-company-001';
Comment on lines 14 to +18
}
const hash = crypto.createHash('sha1').update(username).digest('hex').slice(0, 12);

Check failure

Code scanning / CodeQL

Use of a broken or weak cryptographic algorithm High test

A broken or weak cryptographic algorithm
depends on
sensitive data from an access to username
.
A broken or weak cryptographic algorithm
depends on
sensitive data from an access to username
.
A broken or weak cryptographic algorithm
depends on
sensitive data from an access to username
.
return `test-company-${hash}`;
}
Comment on lines 14 to 22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Align helper declarations with arrow-function guideline.

These helpers are currently function declarations; convert them to const arrow functions for guideline compliance.

Proposed refactor
-function deriveCompanyId(username) {
+const deriveCompanyId = (username) => {
 	// Single-username case (e.g. plain "proxy_user") keeps the legacy id so existing
 	// tests/clients that don't randomize still hit a stable companyId.
 	if (username === PROXY_USERNAME_PREFIX) {
 		return 'test-company-001';
 	}
 	const hash = crypto.createHash('sha1').update(username).digest('hex').slice(0, 12);
 	return `test-company-${hash}`;
-}
+};

-function deriveConnectionId(username) {
+const deriveConnectionId = (username) => {
 	if (username === PROXY_USERNAME_PREFIX) {
 		return 'test-connection-001';
 	}
 	const hash = crypto.createHash('sha1').update(username).digest('hex').slice(0, 12);
 	return `test-connection-${hash}`;
-}
+};

-function deriveHostedDbPlan(level) {
+const deriveHostedDbPlan = (level) => {
 	if (level === 'frozen') return 'frozen';
 	if (level === 'TEST_TINY_PLAN') return 'TEST_TINY_PLAN';
 	if (level === 'HOSTED_DB_FREE' || level === 'FREE_PLAN') return 'HOSTED_DB_FREE';
 	return 'HOSTED_DB_PAID';
-}
+};
As per coding guidelines, "Prefer arrow functions over function declarations".

Also applies to: 24-30, 53-58

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/proxy-mock-api/server.js` around lines 14 - 22, Convert the function
declaration deriveCompanyId into a const arrow function (const deriveCompanyId =
(username) => { ... }) and do the same for the other helper function
declarations flagged in this file (the two other helpers noted in the review);
preserve the existing logic, return values and any references/exports, and
ensure you don't rely on hoisting (move declarations earlier if they are used
before definition).


function deriveConnectionId(username) {
if (username === PROXY_USERNAME_PREFIX) {
return 'test-connection-001';
}
const hash = crypto.createHash('sha1').update(username).digest('hex').slice(0, 12);
return `test-connection-${hash}`;
if (username === PROXY_USERNAME_PREFIX) {
return 'test-connection-001';
}
const hash = crypto.createHash('sha1').update(username).digest('hex').slice(0, 12);

Check failure

Code scanning / CodeQL

Use of a broken or weak cryptographic algorithm High test

A broken or weak cryptographic algorithm
depends on
sensitive data from an access to username
.
A broken or weak cryptographic algorithm
depends on
sensitive data from an access to username
.
A broken or weak cryptographic algorithm
depends on
sensitive data from an access to username
.
return `test-connection-${hash}`;
}

// Upstream connection info returned when the proxy asks for the above client credentials
const TEST_CONNECTION = {
connectionId: 'test-connection-001',
host: process.env.UPSTREAM_PG_HOST || 'testPg-proxy-e2e',
port: parseInt(process.env.UPSTREAM_PG_PORT || '5432', 10),
database: process.env.UPSTREAM_PG_DATABASE || 'postgres',
username: process.env.UPSTREAM_PG_USERNAME || 'postgres',
password: process.env.UPSTREAM_PG_PASSWORD || 'proxy_test_123',
companyId: 'test-company-001',
subscriptionLevel: 'TEAM_PLAN',
connectionId: 'test-connection-001',
host: process.env.UPSTREAM_PG_HOST || 'testPg-proxy-e2e',
port: parseInt(process.env.UPSTREAM_PG_PORT || '5432', 10),
database: process.env.UPSTREAM_PG_DATABASE || 'postgres',
username: process.env.UPSTREAM_PG_USERNAME || 'postgres',
password: process.env.UPSTREAM_PG_PASSWORD || 'proxy_test_123',
companyId: 'test-company-001',
subscriptionLevel: 'TEAM_PLAN',
};

// Configurable subscription level — e2e tests can change this at runtime
// to test throttling / frozen plan behaviour.
let currentSubscriptionLevel = TEST_CONNECTION.subscriptionLevel;

// The proxy keys throttling off hostedDbPlan, not subscriptionLevel. The
// product reality is two-tier — paid (unlimited) and free (throttled) — plus
// the admin-initiated `frozen` override and a TEST_TINY_PLAN used to drive
// the bucket to exhaustion quickly. Anything else is treated as paid so
// positive-path tests run unthrottled.
function deriveHostedDbPlan(level) {
if (level === 'frozen') return 'frozen';
if (level === 'TEST_TINY_PLAN') return 'TEST_TINY_PLAN';
if (level === 'HOSTED_DB_FREE' || level === 'FREE_PLAN') return 'HOSTED_DB_FREE';
return 'HOSTED_DB_PAID';
}

// Store received usage reports so tests can verify them via GET /api/proxy/usage-reports
const usageReports = [];

const server = http.createServer((req, res) => {
// Health check endpoint (no auth required)
if (req.url === '/healthz') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
return;
}

const parsedUrl = url.parse(req.url, true);

// Test-only: configure subscription level at runtime (no auth required)
if (req.method === 'PUT' && parsedUrl.pathname === '/api/test/subscription-level') {
let body = '';
req.on('data', (chunk) => (body += chunk));
req.on('end', () => {
const parsed = JSON.parse(body);
currentSubscriptionLevel = parsed.subscriptionLevel;
console.log(`[mock-api] Subscription level changed to: ${currentSubscriptionLevel}`);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, subscriptionLevel: currentSubscriptionLevel }));
});
return;
}

// Test-only: get collected usage reports (no auth required)
if (req.method === 'GET' && parsedUrl.pathname === '/api/test/usage-reports') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(usageReports));
return;
}

// Test-only: clear collected usage reports (no auth required)
if (req.method === 'DELETE' && parsedUrl.pathname === '/api/test/usage-reports') {
usageReports.length = 0;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
return;
}

const apiKey = req.headers['x-proxy-api-key'];
if (apiKey !== EXPECTED_API_KEY) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unauthorized' }));
return;
}

// GET /api/proxy/connections?username=X&database=Y
if (req.method === 'GET' && parsedUrl.pathname === '/api/proxy/connections') {
const { username, database } = parsedUrl.query;
console.log(`[mock-api] GET connection: username=${username}, database=${database}`);

const usernameMatches =
username === PROXY_USERNAME_PREFIX || (typeof username === 'string' && username.startsWith(PROXY_USERNAME_PREFIX + '_'));

if (usernameMatches && database === PROXY_DATABASE) {
const companyId = deriveCompanyId(username);
const connectionId = deriveConnectionId(username);
console.log(
`[mock-api] -> returning connection, companyId=${companyId} connectionId=${connectionId} subscriptionLevel=${currentSubscriptionLevel}`,
);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
...TEST_CONNECTION,
connectionId,
companyId,
subscriptionLevel: currentSubscriptionLevel,
}),
);
} else {
console.log(
`[mock-api] -> 404: username/database mismatch (expected ${PROXY_USERNAME_PREFIX}[_*]/${PROXY_DATABASE})`,
);
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Connection not found' }));
}
return;
}

// POST /api/proxy/usage
if (req.method === 'POST' && parsedUrl.pathname === '/api/proxy/usage') {
let body = '';
req.on('data', (chunk) => (body += chunk));
req.on('end', () => {
console.log(`[mock-api] POST usage: ${body}`);
try {
const reports = JSON.parse(body);
if (Array.isArray(reports)) {
usageReports.push(...reports);
} else {
usageReports.push(reports);
}
} catch (e) {
console.error(`[mock-api] Failed to parse usage body: ${e.message}`);
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
return;
}

res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
// Health check endpoint (no auth required)
if (req.url === '/healthz') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
return;
}

const parsedUrl = url.parse(req.url, true);

// Test-only: configure subscription level at runtime (no auth required)
if (req.method === 'PUT' && parsedUrl.pathname === '/api/test/subscription-level') {
let body = '';
req.on('data', (chunk) => (body += chunk));
req.on('end', () => {
const parsed = JSON.parse(body);
currentSubscriptionLevel = parsed.subscriptionLevel;
console.log(`[mock-api] Subscription level changed to: ${currentSubscriptionLevel}`);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true, subscriptionLevel: currentSubscriptionLevel }));
Comment on lines +74 to +82
});
Comment on lines +74 to +83

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle malformed JSON explicitly in subscription-level endpoint.

JSON.parse(body) on Line 78 is unguarded; malformed input can throw and break this request path. Return a 400 with an error payload instead.

Proposed fix
 	if (req.method === 'PUT' && parsedUrl.pathname === '/api/test/subscription-level') {
 		let body = '';
 		req.on('data', (chunk) => (body += chunk));
 		req.on('end', () => {
-			const parsed = JSON.parse(body);
-			currentSubscriptionLevel = parsed.subscriptionLevel;
-			console.log(`[mock-api] Subscription level changed to: ${currentSubscriptionLevel}`);
-			res.writeHead(200, { 'Content-Type': 'application/json' });
-			res.end(JSON.stringify({ ok: true, subscriptionLevel: currentSubscriptionLevel }));
+			try {
+				const parsed = JSON.parse(body);
+				currentSubscriptionLevel = parsed.subscriptionLevel;
+				console.log(`[mock-api] Subscription level changed to: ${currentSubscriptionLevel}`);
+				res.writeHead(200, { 'Content-Type': 'application/json' });
+				res.end(JSON.stringify({ ok: true, subscriptionLevel: currentSubscriptionLevel }));
+			} catch (e) {
+				res.writeHead(400, { 'Content-Type': 'application/json' });
+				res.end(JSON.stringify({ error: 'Invalid JSON body' }));
+			}
 		});
 		return;
 	}
As per coding guidelines, "Ensure all error handling is explicit - use try/catch blocks appropriately".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/proxy-mock-api/server.js` around lines 74 - 83, The unguarded JSON.parse
in the PUT handler for '/api/test/subscription-level' can throw on malformed
input; wrap the body parsing/JSON.parse in a try/catch inside the req.on('end')
callback, and on catch respond with res.writeHead(400, { 'Content-Type':
'application/json' }) and res.end(JSON.stringify({ ok: false, error: 'Invalid
JSON' })) so the server doesn't crash; on success continue to set
currentSubscriptionLevel and return the existing 200 response.

return;
}

// Test-only: get collected usage reports (no auth required)
if (req.method === 'GET' && parsedUrl.pathname === '/api/test/usage-reports') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(usageReports));
return;
}

// Test-only: clear collected usage reports (no auth required)
if (req.method === 'DELETE' && parsedUrl.pathname === '/api/test/usage-reports') {
usageReports.length = 0;
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
return;
}

const apiKey = req.headers['x-proxy-api-key'];
if (apiKey !== EXPECTED_API_KEY) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unauthorized' }));
return;
}

// GET /api/proxy/connections?username=X&database=Y
if (req.method === 'GET' && parsedUrl.pathname === '/api/proxy/connections') {
const { username, database } = parsedUrl.query;
console.log(`[mock-api] GET connection: username=${username}, database=${database}`);

const usernameMatches =
username === PROXY_USERNAME_PREFIX ||
(typeof username === 'string' && username.startsWith(PROXY_USERNAME_PREFIX + '_'));

if (usernameMatches && database === PROXY_DATABASE) {
const companyId = deriveCompanyId(username);
const connectionId = deriveConnectionId(username);
const hostedDbPlan = deriveHostedDbPlan(currentSubscriptionLevel);
console.log(
`[mock-api] -> returning connection, companyId=${companyId} connectionId=${connectionId} subscriptionLevel=${currentSubscriptionLevel} hostedDbPlan=${hostedDbPlan}`,
);
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
...TEST_CONNECTION,
connectionId,
companyId,
subscriptionLevel: currentSubscriptionLevel,
hostedDbPlan,
}),
);
} else {
console.log(
`[mock-api] -> 404: username/database mismatch (expected ${PROXY_USERNAME_PREFIX}[_*]/${PROXY_DATABASE})`,

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High test

This logs sensitive data returned by
process environment
as clear text.
);
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Connection not found' }));
}
return;
}

// POST /api/proxy/usage
if (req.method === 'POST' && parsedUrl.pathname === '/api/proxy/usage') {
let body = '';
req.on('data', (chunk) => (body += chunk));
req.on('end', () => {
console.log(`[mock-api] POST usage: ${body}`);
try {
const reports = JSON.parse(body);
if (Array.isArray(reports)) {
usageReports.push(...reports);
} else {
usageReports.push(reports);
}
} catch (e) {
console.error(`[mock-api] Failed to parse usage body: ${e.message}`);
}
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ ok: true }));
});
return;
}

res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not found' }));
});

server.listen(PORT, () => {
console.log(`[mock-api] Proxy mock API listening on port ${PORT}`);
console.log(`[mock-api] Proxy mock API listening on port ${PORT}`);
});
Loading