Skip to content
Open
Show file tree
Hide file tree
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
34 changes: 34 additions & 0 deletions services/09-commerce-engine/security.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,38 @@ describe('L9 Commerce Engine Security Tests', () => {
);
});
});

describe('JWT Weak Secret Production Rejection', () => {
const originalEnv = process.env.NODE_ENV;
const originalJwtSecret = process.env.JWT_SECRET;

afterEach(() => {
process.env.NODE_ENV = originalEnv;
process.env.JWT_SECRET = originalJwtSecret;
});

test('should return 500 in production when weak secret is used in auth middleware', async () => {
process.env.NODE_ENV = 'production';
process.env.JWT_SECRET = 'dev_secret_change_in_production';

const { authenticateToken } = require('./src/utils/auth');

const req = {
headers: {
authorization: `Bearer ${fleetAToken}`
}
};
const res = {
status: jest.fn().mockReturnThis(),
json: jest.fn()
};
const next = jest.fn();

authenticateToken(req, res, next);

expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalledWith({ error: 'Internal server configuration error' });
expect(next).not.toHaveBeenCalled();
});
});
});
21 changes: 18 additions & 3 deletions services/09-commerce-engine/src/utils/auth.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
const jwt = require('jsonwebtoken');
const { jwtSecret } = require('../../config');
const config = require('../../config');

const WEAK_SECRETS = ['dev_secret_change_in_production', 'test_secret', 'dev_secret', 'default_secret', 'secret'];

const isWeakSecret = (secret) => {
if (!secret) return true;
return WEAK_SECRETS.includes(secret.toLowerCase().trim());
};

const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
Expand All @@ -9,12 +16,20 @@ const authenticateToken = (req, res, next) => {
return res.status(401).json({ error: 'Access token required' });
}

if (!jwtSecret || jwtSecret === 'dev_secret_change_in_production') {
const activeSecret = process.env.JWT_SECRET || config.jwtSecret;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Env secret bypasses config

Medium Severity

authenticateToken resolves the secret as process.env.JWT_SECRET || config.jwtSecret, so a live env value always wins over config.jwtSecret. Existing suites that mock config and sign tokens with the mock secret (for example tests/security.test.js) can hit 403 whenever JWT_SECRET is set in the process environment, including from another test file or a developer shell.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 59d93b8. Configure here.


if (!activeSecret) {
console.error('[Security] JWT_SECRET is not properly configured.');
return res.status(500).json({ error: 'Internal server configuration error' });
}

jwt.verify(token, jwtSecret, (err, user) => {
// [Security Hardening] Reject weak secrets in production environment
if (process.env.NODE_ENV === 'production' && isWeakSecret(activeSecret)) {
console.error('[Security] JWT_SECRET is weak, insecure, or default. Blocking authenticated endpoint access in production.');
return res.status(500).json({ error: 'Internal server configuration error' });
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Default-secret test now fails

Medium Severity

Weak secrets are rejected only when NODE_ENV === 'production', but tests/security.test.js still expects a 500 for dev_secret_change_in_production without setting production. Under Jest (NODE_ENV is test), that request now passes jwt.verify, so npm test fails even though a new production-focused case was added.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 59d93b8. Configure here.


jwt.verify(token, activeSecret, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Invalid or expired token' });
}
Expand Down