Skip to content
Merged
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 AI_FRAUD_DETECTION_PR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# AI-Powered Fraud Detection & Adaptive Defense (Issue #981)

## Summary
This feature deploys a streaming analytics engine with unsupervised machine learning to detect evolving fraud patterns and trigger automated defense actions. It replaces traditional rule-based detection with adaptive, AI-driven mechanisms for real-time fraud prevention.

## Features
- **Streaming analytics engine** for real-time transaction ingestion
- **Unsupervised ML (KMeans clustering)** for anomaly detection
- **Automated defense actions** (block, flag, require 2FA, escalate)
- **Modular dashboard UI** for alerts, defenses, and transaction monitoring
- **Utility functions** for normalization, risk scoring, and formatting
- **Scalable codebase** (500+ lines) for extensibility

## Benefits
- Detects sophisticated, adversarial fraud tactics
- Adapts to evolving fraud patterns without manual rule updates
- Enables automated, rapid defense responses

## Files Added/Modified
- `public/fraud-ml-engine.js`
- `public/fraud-stream-connector.js`
- `public/fraud-defense-actions.js`
- `public/fraud-dashboard.js`
- `public/fraud-dashboard.html`
- `public/fraud-dashboard.css`
- `public/fraud-utils.js`

## How to Use
1. Open `fraud-dashboard.html` in your browser
2. The dashboard will stream transactions, detect anomalies, and display alerts/defense actions in real time

---

Closes #981
30 changes: 30 additions & 0 deletions api/expense-approval-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// expense-approval-api.js
// API server for managing expense approval workflows
const express = require('express');
const bodyParser = require('body-parser');
const { startExpenseApprovalWorkflow, getWorkflowStatus } = require('../temporal/client');

const app = express();
app.use(bodyParser.json());

app.post('/api/expense/submit', async (req, res) => {
try {
const handle = await startExpenseApprovalWorkflow(req.body);
res.json({ workflowId: handle.workflowId });
} catch (err) {
res.status(500).json({ error: err.message });
}
});

app.get('/api/expense/status/:workflowId', async (req, res) => {
try {
const result = await getWorkflowStatus(req.params.workflowId);
res.json({ status: result });
} catch (err) {
res.status(500).json({ error: err.message });
}
});

app.listen(3002, () => {
console.log('Expense Approval API running on http://localhost:3002');
});
20 changes: 20 additions & 0 deletions gateway/adminApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// adminApi.js
// Admin API for dynamic policy management
const express = require('express');
const { updatePolicy, getPolicies } = require('./policyEngine');
const { authenticate } = require('./auth');
const router = express.Router();

router.use(authenticate);

router.get('/admin/policies', (req, res) => {
res.json({ policies: getPolicies() });
});

router.post('/admin/policies/update', (req, res) => {
const { path, policy } = req.body;
updatePolicy(path, policy);
res.json({ success: true });
});

module.exports = router;
27 changes: 27 additions & 0 deletions gateway/advancedPolicyEngine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// advancedPolicyEngine.js
// Advanced policy engine with RBAC, ABAC, and real-time updates
const { getPolicy } = require('./policyStore');

function enforceAdvancedPolicy(req, res, next) {
const policy = getPolicy(req.path);
if (!policy) return next();
// RBAC check
if (policy.roles && !policy.roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Access denied (role)' });
}
// ABAC check
if (policy.attributes) {
for (const attr in policy.attributes) {
if (req.user[attr] !== policy.attributes[attr]) {
return res.status(403).json({ error: `Access denied (attribute: ${attr})` });
}
}
}
// Rate limit override
if (policy.rateLimit && req.rateLimit && req.rateLimit > policy.rateLimit) {
return res.status(429).json({ error: 'Rate limit exceeded (policy)' });
}
next();
}

module.exports = { enforceAdvancedPolicy };
20 changes: 20 additions & 0 deletions gateway/auditTrail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// auditTrail.js
// Audit trail and event logging for API Gateway
const fs = require('fs');
const AUDIT_FILE = 'gateway/audit.log';

function logAudit(event, details) {
const entry = {
timestamp: new Date().toISOString(),
event,
details
};
fs.appendFileSync(AUDIT_FILE, JSON.stringify(entry) + '\n');
}

function getAuditTrail() {
if (!fs.existsSync(AUDIT_FILE)) return [];
return fs.readFileSync(AUDIT_FILE, 'utf-8').split('\n').filter(Boolean).map(line => JSON.parse(line));
}

module.exports = { logAudit, getAuditTrail };
21 changes: 21 additions & 0 deletions gateway/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// auth.js
// Centralized authentication middleware (JWT)
const jwt = require('jsonwebtoken');
const SECRET = 'supersecretkey';

function authenticate(req, res, next) {
const token = req.headers['authorization']?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'Missing token' });
try {
req.user = jwt.verify(token, SECRET);
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}

function issueToken(user) {
return jwt.sign(user, SECRET, { expiresIn: '1h' });
}

module.exports = { authenticate, issueToken };
11 changes: 11 additions & 0 deletions gateway/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// config.js
// Configuration management for gateway
module.exports = {
jwtSecret: 'supersecretkey',
rateLimit: 100,
windowSize: 60000,
serviceMap: {
'/api/serviceA': 'http://localhost:5001',
'/api/serviceB': 'http://localhost:5002'
}
};
34 changes: 34 additions & 0 deletions gateway/gateway.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// gateway.test.js
// Basic tests for API Gateway modules
const { authenticate, issueToken } = require('./auth');
const { rateLimiter } = require('./rateLimiter');
const { enforcePolicy } = require('./policyEngine');
const { enforceAdvancedPolicy } = require('./advancedPolicyEngine');
const { logAudit, getAuditTrail } = require('./auditTrail');
const { validateInput, encodeData, decodeData } = require('./utils');
const assert = require('assert');

function testAuth() {
const token = issueToken({ id: 'user1', role: 'admin' });
assert(token, 'Token should be issued');
}

function testRateLimiter() {
// Simulate requests
let req = { user: { id: 'user1' }, ip: '127.0.0.1' };
let res = { status: code => ({ json: obj => obj }) };
let next = () => true;
for (let i = 0; i < 10; i++) rateLimiter(req, res, next);
}

function testPolicyEngine() {
let req = { path: '/api/serviceA', user: { role: 'admin' } };
let res = { status: code => ({ json: obj => obj }) };
let next = () => true;
enforcePolicy(req, res, next);
}

testAuth();
testRateLimiter();
testPolicyEngine();
console.log('All gateway tests passed.');
8 changes: 8 additions & 0 deletions gateway/logging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// logging.js
// Logging and audit middleware
function logRequest(req, res, next) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path} by ${req.user?.id || req.ip}`);
next();
}

module.exports = { logRequest };
8 changes: 8 additions & 0 deletions gateway/monitoring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// monitoring.js
// Monitoring and health check middleware
function monitor(req, res, next) {
// Simulate metrics collection
next();
}

module.exports = { monitor };
25 changes: 25 additions & 0 deletions gateway/policyEngine.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// policyEngine.js
// Dynamic policy enforcement middleware
let policies = {
'/api/serviceA': { roles: ['admin', 'user'], rateLimit: 50 },
'/api/serviceB': { roles: ['admin'], rateLimit: 20 }
};

function enforcePolicy(req, res, next) {
const policy = policies[req.path];
if (!policy) return next();
if (!policy.roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Access denied' });
}
next();
}

function updatePolicy(path, newPolicy) {
policies[path] = newPolicy;
}

function getPolicies() {
return policies;
}

module.exports = { enforcePolicy, updatePolicy, getPolicies };
45 changes: 45 additions & 0 deletions gateway/policyStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// policyStore.js
// In-memory and persistent policy storage for dynamic enforcement
const fs = require('fs');
const POLICY_FILE = 'gateway/policies.json';

let policies = {};

function loadPolicies() {
if (fs.existsSync(POLICY_FILE)) {
policies = JSON.parse(fs.readFileSync(POLICY_FILE));
}
}

function savePolicies() {
fs.writeFileSync(POLICY_FILE, JSON.stringify(policies, null, 2));
}

function getPolicy(path) {
return policies[path];
}

function setPolicy(path, policy) {
policies[path] = policy;
savePolicies();
}

function deletePolicy(path) {
delete policies[path];
savePolicies();
}

function listPolicies() {
return policies;
}

loadPolicies();

module.exports = {
getPolicy,
setPolicy,
deletePolicy,
listPolicies,
loadPolicies,
savePolicies
};
19 changes: 19 additions & 0 deletions gateway/rateLimiter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// rateLimiter.js
// Rate limiting middleware
const rateLimits = {};
const WINDOW_SIZE = 60000; // 1 minute
const MAX_REQUESTS = 100;

function rateLimiter(req, res, next) {
const userId = req.user?.id || req.ip;
const now = Date.now();
if (!rateLimits[userId]) rateLimits[userId] = [];
rateLimits[userId] = rateLimits[userId].filter(ts => now - ts < WINDOW_SIZE);
if (rateLimits[userId].length >= MAX_REQUESTS) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
rateLimits[userId].push(now);
next();
}

module.exports = { rateLimiter };
19 changes: 19 additions & 0 deletions gateway/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// routes.js
// Request routing and microservice proxying
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer({});

const serviceMap = {
'/api/serviceA': 'http://localhost:5001',
'/api/serviceB': 'http://localhost:5002'
};

function routeRequest(req, res) {
const target = serviceMap[req.path];
if (!target) return res.status(404).json({ error: 'Service not found' });
proxy.web(req, res, { target }, err => {
res.status(502).json({ error: 'Proxy error', details: err.message });
});
}

module.exports = { routeRequest };
23 changes: 23 additions & 0 deletions gateway/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// server.js
// Main API Gateway server
const express = require('express');
const bodyParser = require('body-parser');
const { authenticate } = require('./auth');
const { rateLimiter } = require('./rateLimiter');
const { enforcePolicy } = require('./policyEngine');
const { routeRequest } = require('./routes');
const { logRequest } = require('./logging');
const { monitor } = require('./monitoring');

const app = express();
app.use(bodyParser.json());
app.use(logRequest);
app.use(monitor);
app.use(authenticate);
app.use(rateLimiter);
app.use(enforcePolicy);
app.use(routeRequest);

app.listen(4000, () => {
console.log('API Gateway running on http://localhost:4000');
});
20 changes: 20 additions & 0 deletions gateway/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// utils.js
// Utility functions for gateway
function validateInput(input, schema) {
// Simulate input validation
return true;
}

function encodeData(data) {
return Buffer.from(JSON.stringify(data)).toString('base64');
}

function decodeData(encoded) {
return JSON.parse(Buffer.from(encoded, 'base64').toString());
}

function handleError(res, error) {
res.status(500).json({ error: error.message });
}

module.exports = { validateInput, encodeData, decodeData, handleError };
Loading