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
45 changes: 36 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,17 @@ RetailShield closes that gap.
┌─────────────────────────────────────────────────────────────────┐
│ 2. DETECTION — 20 KQL rules mapped to MITRE ATT&CK │
│ 2. DETECTION — 24 KQL rules mapped to MITRE ATT&CK │
│ │
│ Retail-specific (14): gift-card fraud · POS void/refund · │
│ credential stuffing · MFA fatigue · phishing · ransomware · │
│ supplier compromise · data exfil · AI voice fraud · POS │
│ anomaly · privileged role abuse · after-hours · impossible │
│ travel · TLS downgrade (PCI) │
│ │
│ Loss Prevention (4): void/refund abuse · gift card fraud · │
│ sweethearting · after-hours POS transaction │
│ │
│ Generic SOC (6): brute force · bulk file access · C2 beacon · │
│ DNS exfil · RDP lateral movement · suspicious PowerShell │
└─────────────────────────────────────────────────────────────────┘
Expand All @@ -119,7 +122,7 @@ RetailShield closes that gap.
└─────────────────────────────────────────────────────────────────┘

MODULES: [Threat Detection: LIVE] [Compliance Centre: LIVE] [Vulnerability Scanner: LIVE]
[Loss Prevention: PLANNED] [ChainShield: PLANNED]
[Loss Prevention: LIVE] [ChainShield: PLANNED]

Validated in a controlled lab · published methodology (DOI 10.5281/zenodo.20608262) · avg ~22 min MTTD
A Sentinel-native content pack — not a standalone SIEM.
Expand All @@ -131,8 +134,8 @@ A Sentinel-native content pack — not a standalone SIEM.

| Content type | Count | Description |
|---|---|---|
| **KQL Analytics Rules** | 13 retail + 6 generic | Scheduled analytics rules covering POS fraud, ransomware, exfiltration, identity abuse, supply chain, voice fraud |
| **Logic App Playbooks** | 3 | Triage & classify, threat-intel enrichment (AbuseIPDB / VirusTotal), containment (block IP / disable account / isolate host) |
| **KQL Analytics Rules** | 18 retail + 6 generic | Scheduled analytics rules covering POS fraud, ransomware, exfiltration, identity abuse, supply chain, voice fraud, and loss prevention |
| **Logic App Playbooks** | 4 | Triage & classify, threat-intel enrichment (AbuseIPDB / VirusTotal), containment (block IP / disable account / isolate host), LP incident response |
| **Sentinel Workbook** | 1 | Live incident feed, TTP heatmap, analyst KPIs |
| **Watchlists** | 5 | RetailIOCWatchlist, RetailApprovedSenders, AbuseIPDBWatchlist, RetailSupplierAccounts, RetailServiceAccounts |
| **Hunting Queries** | Planned | Proactive threat hunting queries for retail TTPs |
Expand All @@ -158,6 +161,16 @@ A Sentinel-native content pack — not a standalone SIEM.
| Initial Access | T1195 | Supply Chain Compromise | `retail/supply_chain_anomaly.kql` | notify_soc |
| Initial Access | T1199 / T1078 | Trusted Relationship / Valid Accounts | `retail/supplier_impossible_travel.kql` | notify_soc |
| Persistence | T1098 / T1078 | Account Manipulation / Valid Accounts | `retail/privileged_role_addition.kql` | notify_soc |
| Credential Access / Collection | T1557 | Adversary-in-the-Middle | `retail/tls_downgrade_pos.kql` | notify_soc |

### Loss Prevention rules

| Tactic | Technique ID | Technique Name | Detection Rule | Playbook |
|---|---|---|---|---|
| Impact | T1657 | Financial Theft | `retail/lp-pos-void-refund-abuse.kql` | lp-incident-response |
| Impact | T1657 | Financial Theft | `retail/lp-gift-card-rapid-redemption.kql` | lp-incident-response |
| Impact | T1657 | Financial Theft | `retail/lp-sweethearting.kql` | lp-incident-response |
| Impact | T1657 | Financial Theft | `retail/lp-after-hours-pos-transaction.kql` | lp-incident-response |

### Generic rules

Expand Down Expand Up @@ -194,7 +207,12 @@ RetailShield/
│ │ ├── ransomware_indicator.kql # RS-RAN-001 — T1486 — Critical
│ │ ├── supply_chain_anomaly.kql # RS-SUP-001 — T1195 — High
│ │ ├── supplier_impossible_travel.kql # RS-SUP-002 — T1199 — Medium
│ │ └── privileged_role_addition.kql # RS-PRA-001 — T1098 — High
│ │ ├── privileged_role_addition.kql # RS-PRA-001 — T1098 — High
│ │ ├── tls_downgrade_pos.kql # RS-TLS-001 — T1557 — High
│ │ ├── lp-pos-void-refund-abuse.kql # LP-001 — T1657 — High
│ │ ├── lp-gift-card-rapid-redemption.kql # LP-002 — T1657 — High
│ │ ├── lp-sweethearting.kql # LP-003 — T1657 — High
│ │ └── lp-after-hours-pos-transaction.kql # LP-004 — T1657 — High
│ ├── generic/ # General-purpose SOC rules
│ │ ├── brute-force-login.kql # GEN-001 — T1110
│ │ ├── bulk-file-access.kql # GEN-002 — T1005
Expand All @@ -212,6 +230,8 @@ RetailShield/
│ ├── containment/
│ │ ├── workflow.json # Block IP / Disable account / Isolate host
│ │ └── README.md
│ ├── lp-incident-response/
│ │ └── workflow.json # LP incident triage, manager notification, HR/CCTV escalation
│ └── DEPLOYMENT.md # Step-by-step Logic App deployment guide
├── sentinel/
Expand All @@ -232,11 +252,13 @@ RetailShield/
│ ├── validate_kql.py # KQL rule static validator (used by CI)
│ ├── validate_logicapps.py # Logic App JSON validator (used by CI)
│ ├── retail_log_generator.py # Sample retail log generator for testing
│ └── cve_scanner.py # CVE scanner utility
│ ├── cve_scanner.py # CVE scanner utility
│ └── deploy_all.py # One-command deployment to Sentinel workspace
├── tests/
│ ├── detection-rules/
│ │ └── test_kql_rules.py
│ │ ├── test_kql_rules.py
│ │ └── test_lp_rules.py # 94 tests covering all 4 LP detection rules
│ └── playbooks/
│ └── test_playbook_schema.py
Expand Down Expand Up @@ -268,9 +290,14 @@ git checkout dev

### 2. Deploy KQL analytics rules to Sentinel

Rules are deployed manually through the Microsoft Sentinel Analytics blade. There is no automated deployment script at this time.
Rules can be deployed via the included script (requires `az login` and ARM rule templates in `sentinel/analytics-rules/`):

```bash
pip install azure-identity azure-mgmt-securityinsight
python scripts/deploy_all.py --workspace <workspace-name> --resource-group <rg-name> --dry-run
```

For each `.kql` file in `detection-rules/retail/` (and optionally `detection-rules/generic/`):
Or deploy manually through the Microsoft Sentinel Analytics blade. For each `.kql` file in `detection-rules/retail/` (and optionally `detection-rules/generic/`):

1. In the Azure Portal, open your Sentinel workspace → **Analytics** → **+ Create** → **Scheduled query rule**
2. Set the rule name and description using the `// Rule ID` and `// Title` comments at the top of the file
Expand Down
20 changes: 11 additions & 9 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ Research paper: [DOI 10.5281/zenodo.20608262](https://doi.org/10.5281/zenodo.206
The foundation: a working SOC platform purpose-built for UK retail, with published research validating the detection capability.

### Detection engineering
- [x] 19 production-ready KQL detection rules covering the full MITRE ATT&CK kill chain
- [x] 8 Logic App playbooks for automated incident response
- [x] 24 production-ready KQL detection rules covering the full MITRE ATT&CK kill chain
- [x] 9 Logic App playbooks for automated incident response
- [x] Sentinel analytics-rule JSON wrappers for one-click deployment
- [x] 221 automated pytest tests validating rule syntax and MITRE mapping
- [x] 315 automated pytest tests validating rule syntax and MITRE mapping
- [x] One-command deployment script (`scripts/deploy_all.py`) for Sentinel workspace

### Compliance automation
- [x] ICO 72-hour breach notification countdown with auto-generated draft reports
Expand All @@ -23,11 +24,12 @@ The foundation: a working SOC platform purpose-built for UK retail, with publish
- [x] Submission history tracking

### Platform
- [x] Responsive portal dashboard (Landing → Login → Portal → 4 modules)
- [x] Responsive portal dashboard (Landing → Login → Portal → 5 modules)
- [x] Threat Detection module: live incident feed, MITRE heatmap, attack timeline, AI-generated incident reports
- [x] Vulnerability Scanner: SVG risk gauge, phased scan progress, OWASP Top 10 findings
- [x] Compliance Centre: live countdown timers, regulatory accordion, submission log
- [x] Detection Rules: 19-rule table with search/filter, full MITRE ATT&CK coverage matrix
- [x] Detection Rules: 24-rule table with search/filter, full MITRE ATT&CK coverage matrix
- [x] Loss Prevention module: store risk leaderboard, incident detail panel, void/refund/gift card/sweethearting signals
- [x] Fully responsive at 375px, 768px, 1024px, and 1440px viewport widths
- [x] SIMULATE ATTACK feature populating all modules with realistic incident data

Expand All @@ -42,10 +44,10 @@ The foundation: a working SOC platform purpose-built for UK retail, with publish
Expanding coverage to retail-specific financial crime and supply chain risk, and connecting the platform to live Sentinel data.

### Loss Prevention module
- [ ] POS transaction anomaly detection (void abuse, refund fraud, sweethearting patterns)
- [ ] Gift card abuse tracking (bulk activation, rapid redemption sequences)
- [ ] Store risk scoring dashboard with trend analysis
- [ ] Integration with retail EPOS event logs
- [x] POS transaction anomaly detection (void abuse, refund fraud, sweethearting patterns)
- [x] Gift card abuse tracking (bulk activation, rapid redemption sequences)
- [x] Store risk scoring dashboard with trend analysis
- [x] Integration with retail EPOS event logs

### ChainShield — Supply chain security module
- [ ] Third-party supplier compromise detection
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react';
import { BASELINE_INCIDENTS } from './lib/data.js';
import { BASELINE_INCIDENTS, LP_INCIDENTS } from './lib/data.js';
import Landing from './pages/Landing.jsx';
import Login from './pages/Login.jsx';
import Portal from './pages/Portal.jsx';
Expand All @@ -10,8 +10,9 @@ import DetectionRules from './modules/DetectionRules.jsx';
import LossPrevention from './modules/LossPrevention.jsx';

export default function App() {
const [route, setRoute] = useState('landing');
const [incidents, setIncidents] = useState([...BASELINE_INCIDENTS]);
const [route, setRoute] = useState('landing');
const [incidents, setIncidents] = useState([...BASELINE_INCIDENTS]);
const [lpIncidents, setLpIncidents] = useState([...LP_INCIDENTS]);

const nav = (page) => {
setRoute(page);
Expand All @@ -28,7 +29,7 @@ export default function App() {
case 'vuln': return <VulnerabilityScanner nav={nav} onBack={() => nav('portal')} />;
case 'compliance': return <ComplianceCentre nav={nav} incidents={incidents} onBack={() => nav('portal')} />;
case 'rules': return <DetectionRules nav={nav} onBack={() => nav('portal')} />;
case 'lp': return <LossPrevention nav={nav} onBack={() => nav('portal')} />;
case 'lp': return <LossPrevention nav={nav} onBack={() => nav('portal')} lpIncidents={lpIncidents} setLpIncidents={setLpIncidents} />;
default: return <Landing onDemo={() => nav('portal')} onSignIn={() => nav('login')} />;
}
}
123 changes: 123 additions & 0 deletions frontend/src/lib/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,129 @@ export const LP_STORE_RISK = [
{ storeId: 'Store-011', storeName: 'Lakeside', riskScore: 28, openIncidents: 0, highestSignal: null, lastIncident: null, trend: 'stable'},
{ storeId: 'Store-055', storeName: 'Arndale', riskScore: 21, openIncidents: 0, highestSignal: null, lastIncident: null, trend: 'down' },
];
export const LP_ATTACK_SIM_EVENTS = [
{
id: 'LP-SIM-001',
title: 'Void Abuse Spike — Till Override Bypass',
severity: 'Critical',
tactic: 'Impact',
technique: 'T1657',
status: 'Active',
detectedAt: null,
mttd: 1,
ruleId: 'LP-001',
detectionSignal: 'HighValueVoidNoOverride',
storeId: 'Store-044',
operatorId: 'EMP-7732',
terminalId: 'POS-044-02',
transactionCount: 6,
totalValueGBP: 1840.00,
hasManagerOverride: false,
riskScore: 97,
description: 'Simulated: EMP-7732 processed 6 high-value void transactions totalling £1,840 at Store-044 with no manager override. Matches known till manipulation pattern.',
timeline: [
{ time: '+0:00', event: 'First high-value void — £310 — no manager override at POS-044-02' },
{ time: '+0:04', event: 'Third void — running total exceeds £500 critical threshold' },
{ time: '+0:08', event: 'Sixth void — total reaches £1,840' },
{ time: '+0:08', event: 'Critical alert — HighValueVoidNoOverride, RiskScore 97' },
],
affectedEntities: ['EMP-7732', 'POS-044-02', 'Store-044 refund ledger'],
autoDefence: ['Operator account suspended immediately', 'Manager PIN enforcement activated', 'LP team escalated'],
recommendations: ['Same-day investigation and reconciliation', 'Review 7-day void history for EMP-7732', 'Preserve CCTV footage'],
estimatedLossGBP: 1840.00,
},
{
id: 'LP-SIM-002',
title: 'Organised Gift Card Fraud — 14 Cards in 25 Minutes',
severity: 'Critical',
tactic: 'Impact',
technique: 'T1657',
status: 'Active',
detectedAt: null,
mttd: 2,
ruleId: 'LP-002',
detectionSignal: 'RapidCrossChannelRedemption',
storeId: 'Store-012',
operatorId: 'EMP-5521',
terminalId: 'POS-012-01',
activationCount: 14,
totalValueGBP: 700.00,
minutesSinceActivation: 12,
riskScore: 96,
description: 'Simulated: 14 gift cards activated at Store-012 and redeemed online within 12 minutes of activation. Consistent with organised retail gift card fraud ring.',
timeline: [
{ time: '+0:00', event: '14 gift cards activated at Store-012 POS-012-01 by EMP-5521' },
{ time: '+0:12', event: 'All 14 cards redeemed via online channel — 12 minutes post-activation' },
{ time: '+0:12', event: 'RapidCrossChannelRedemption fired — Critical, RiskScore 96' },
{ time: '+0:13', event: 'Cards suspended; fraud team alerted' },
],
affectedEntities: ['EMP-5521', 'POS-012-01', '14 gift card accounts', 'Online redemption API'],
autoDefence: ['Affected cards frozen', 'Online channel velocity cap applied', 'Fraud team alerted'],
recommendations: ['Coordinate with online fraud team to trace redemption session', 'Investigate whether EMP-5521 acted alone', 'Consider law enforcement referral'],
estimatedLossGBP: 700.00,
},
{
id: 'LP-SIM-003',
title: 'Sweethearting — 47% Discount to Repeat Customer Over 5 Days',
severity: 'High',
tactic: 'Impact',
technique: 'T1657',
status: 'Investigating',
detectedAt: null,
mttd: 18,
ruleId: 'LP-003',
detectionSignal: 'RepeatHighDiscountRelationship',
storeId: 'Store-029',
operatorId: 'EMP-4482',
loyaltyCardId: 'LC-20019447',
transactionCount: 11,
avgDiscountRate: 0.47,
totalDiscountGBP: 421.60,
avgBasketGBP: 34.22,
riskScore: 82,
description: 'Simulated: Cashier EMP-4482 served loyalty customer LC-20019447 eleven times in 5 days with an average discount of 47% — well above the 30% threshold. No loyalty entitlement found.',
timeline: [
{ time: 'Day 1', event: 'First transaction — 51% discount applied by EMP-4482' },
{ time: 'Days 2–4', event: '10 further transactions — discounts between 41% and 53%' },
{ time: 'Day 5', event: '5-day rolling analysis triggers RepeatHighDiscountRelationship' },
{ time: 'Day 5 +18min', event: 'LP manager notified; HR review scheduled' },
],
affectedEntities: ['EMP-4482', 'LC-20019447', 'Store-029 discount ledger'],
autoDefence: ['Discount cap applied to EMP-4482 terminal', 'LP manager alerted', 'Transaction history exported for HR'],
recommendations: ['Full 30-day discount audit for EMP-4482', 'Cross-check LC-20019447 loyalty entitlement', 'Review CCTV to confirm scanning compliance'],
estimatedLossGBP: 421.60,
},
{
id: 'LP-SIM-004',
title: 'After-Hours Ghost Transaction — 03:17 AM — Store-061',
severity: 'Critical',
tactic: 'Impact',
technique: 'T1657',
status: 'Active',
detectedAt: null,
mttd: 1,
ruleId: 'LP-004',
detectionSignal: 'GhostEmployeeAfterHours',
storeId: 'Store-061',
operatorId: 'EMP-1147',
terminalId: 'POS-061-01',
transactionCount: 7,
totalValueGBP: 934.25,
hourOfDay: 3,
riskScore: 95,
description: 'Simulated: EMP-1147 processed 7 transactions totalling £934.25 at 03:17 AM with no active shift record. Potential ghost employee or credential-sharing incident.',
timeline: [
{ time: '03:11', event: 'EMP-1147 logs into POS-061-01 — no shift record found' },
{ time: '03:17', event: '7 transactions processed totalling £934.25' },
{ time: '03:17', event: 'GhostEmployeeAfterHours alert — Critical, RiskScore 95' },
{ time: '03:18', event: 'Terminal locked remotely; security dispatched' },
],
affectedEntities: ['EMP-1147', 'POS-061-01', 'Store-061 till'],
autoDefence: ['Terminal remotely locked', 'Security team dispatched', 'EMP-1147 credential suspended'],
recommendations: ['Contact on-site security and review CCTV immediately', 'Verify badge access records for EMP-1147', 'Consider law enforcement notification if entry confirmed'],
estimatedLossGBP: 934.25,
},
];

export const SUBMISSION_HISTORY = [
{ id: 'SUB-001', date: '2026-05-22T10:30:00Z', incident: 'INC-2801', regulation: 'UK GDPR', authority: 'ICO', status: 'Submitted', refNo: 'ICO-2026-051822' },
Expand Down
Loading
Loading