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
12 changes: 12 additions & 0 deletions services/01-physics-engine/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ const app = express();
const port = process.env.PORT || 3001;
const JWT_SECRET = process.env.JWT_SECRET || 'dev_secret_change_in_production';

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 DATABASE_URL = process.env.DATABASE_URL;
const REDIS_URL = process.env.REDIS_URL;
const KAFKA_BROKERS = (process.env.KAFKA_BROKERS || 'localhost:9092').split(',');
Expand Down Expand Up @@ -68,6 +74,12 @@ app.use(express.json());
* Middleware: Verify JWT token (Zero-Trust Security)
*/
const authenticateToken = (req, res, next) => {
// [Security Hardening] Reject weak secrets in production environment
if (process.env.NODE_ENV === 'production' && isWeakSecret(JWT_SECRET)) {
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' });
}

const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];

Expand Down
118 changes: 118 additions & 0 deletions services/01-physics-engine/security.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
const request = require('supertest');
const jwt = require('jsonwebtoken');

// Mock pg
const mockClient = {
connect: jest.fn(),
query: jest.fn(),
end: jest.fn(),
on: jest.fn()
};
jest.mock('pg', () => {
return { Client: jest.fn(() => mockClient) };
});

// Mock kafkajs
const mockProducer = {
connect: jest.fn(),
send: jest.fn(),
disconnect: jest.fn()
};
const mockConsumer = {
connect: jest.fn(),
subscribe: jest.fn(),
run: jest.fn(),
disconnect: jest.fn()
};
const mockKafka = {
producer: jest.fn(() => mockProducer),
consumer: jest.fn(() => mockConsumer)
};
jest.mock('kafkajs', () => {
return { Kafka: jest.fn(() => mockKafka) };
});

// Mock redis
const mockRedisClient = {
connect: jest.fn(),
get: jest.fn(),
set: jest.fn(),
setEx: jest.fn(),
hGetAll: jest.fn(),
scan: jest.fn(() => ({ cursor: '0', keys: [] })),
quit: jest.fn()
};
jest.mock('redis', () => ({
createClient: jest.fn(() => mockRedisClient)
}));

describe('L1 Physics Engine Security Hardening', () => {
let originalEnv;

beforeAll(() => {
originalEnv = { ...process.env };
});

afterEach(() => {
process.env = { ...originalEnv };
jest.resetModules();
});

test('GET /health should return 200 and run successfully without authentication', async () => {
const { app } = require('./index');
const res = await request(app).get('/health');
expect(res.status).toBe(200);
expect(res.body.service).toBe('physics-engine');
});

test('Authenticated route should fail securely with 500 when NODE_ENV is production and JWT_SECRET is weak', async () => {
process.env.NODE_ENV = 'production';
process.env.JWT_SECRET = 'dev_secret_change_in_production'; // weak secret

const { app } = require('./index');
const token = jwt.sign({ driver_id: 'driver-123', fleet_id: 'fleet-abc' }, 'dev_secret_change_in_production');

const res = await request(app)
.get('/data/training/physics')
.set('Authorization', `Bearer ${token}`);

expect(res.status).toBe(500);
expect(res.body.error).toBe('Internal server configuration error');
});

test('Authenticated route should verify token correctly when NODE_ENV is production and JWT_SECRET is strong', async () => {
process.env.NODE_ENV = 'production';
const strongSecret = 'super_strong_unpredictable_production_secret_key_12345';
process.env.JWT_SECRET = strongSecret;

mockClient.query.mockResolvedValueOnce({
rows: [{
session_id: 'session-123',
violation_type: 'EFFICIENCY_ALERT',
expected_value: 0.85,
actual_value: 0.70,
severity: 'WARNING',
metadata: {},
billing_mode: 'FLEET',
vpp_active: true,
v2g_active: false,
iso_region: 'CAISO',
market_price_at_session: 50.0,
physics_score: 0.80,
is_high_fidelity: true,
created_at: new Date().toISOString()
}]
});

const { app } = require('./index');
const token = jwt.sign({ sub: 'admin' }, strongSecret); // System token (no fleet_id)

const res = await request(app)
.get('/data/training/physics')
.set('Authorization', `Bearer ${token}`);

expect(res.status).not.toBe(500);
expect(res.status).toBe(200);
expect(res.body.record_count).toBe(1);
});
});