Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/daffy0208/ai-dev-standards --skill forensic-data-engineer- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads forensic-data-engineer automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Forensic Data Engineer Skill
I help you investigate data anomalies, detect fraud, analyze audit trails, and ensure data integrity and compliance.
What I Do
Forensic Analysis:
- Anomaly detection and pattern recognition
- Fraud detection and prevention
- Breach investigation and root cause analysis
- Data integrity verification
Audit & Compliance:
- Audit trail analysis and reconstruction
- Chain of custody maintenance
- Regulatory compliance (GDPR, SOC2, HIPAA)
- Access control auditing
Data Recovery:
- Forensic recovery of deleted data
- Historical data reconstruction
- Change detection and unauthorized modifications
- Data lineage and provenance tracking
Forensic Patterns
Pattern 1: Audit Trail Implementation
Use case: Track all data changes for compliance and investigation
// lib/forensics/audit-trail.ts
interface AuditEntry {
id: string
timestamp: Date
userId: string
action: 'CREATE' | 'UPDATE' | 'DELETE' | 'READ'
tableName: string
recordId: string
oldValue?: any
newValue?: any
ipAddress: string
userAgent: string
sessionId: string
}
export async function createAuditLog(entry: Omit<AuditEntry, 'id' | 'timestamp'>) {
return await db.auditLog.create({
data: {
...entry,
timestamp: new Date()
}
})
}
// Middleware for automatic audit logging
export function withAudit<T extends (...args: any[]) => Promise<any>>(
operation: T,
metadata: { tableName: string; action: AuditEntry['action'] }
): T {
return (async (...args: any[]) => {
const startTime = Date.now()
const { tableName, action } = metadata
try {
// Capture before state for UPDATE/DELETE
let oldValue
if (action === 'UPDATE' || action === 'DELETE') {
oldValue = await captureCurrentState(tableName, args[0])
}
// Execute operation
const result = await operation(...args)
// Capture after state
const newValue = action !== 'DELETE' ? result : null
// Log audit entry
await createAuditLog({
userId: getCurrentUser().id,
action,
tableName,
recordId: args[0],
oldValue,
newValue,
ipAddress: getClientIp(),
userAgent: getClientUserAgent(),
sessionId: getSessionId()
})
return result
} catch (error) {
// Log failed attempt
await createAuditLog({
userId: getCurrentUser().id,
action,
tableName,
recordId: args[0],
ipAddress: getClientIp(),
userAgent: getClientUserAgent(),
sessionId: getSessionId()
})
throw error
}
}) as T
}
// Usage
const updateUser = withAudit(
async (userId: string, data: any) => {
return await db.user.update({
where: { id: userId },
data
})
},
{ tableName: 'users', action: 'UPDATE' }
)
Pattern 2: Anomaly Detection
Use case: Identify suspicious patterns in transaction data
// lib/forensics/anomaly-detection.ts
interface Transaction {
id: string
userId: string
amount: number
timestamp: Date
location: string
deviceId: string
}
export async function detectTransactionAnomalies(transaction: Transaction) {
const anomalies: string[] = []
// Check 1: Unusual amount (statistical outlier)
const userStats = await getUserTransactionStats(transaction.userId)
const zScore = (transaction.amount - userStats.mean) / userStats.stdDev
if (Math.abs(zScore) > 3) {
anomalies.push(`Unusual amount: ${transaction.amount} (z-score: ${zScore.toFixed(2)})`)
}
// Check 2: Rapid succession (velocity check)
const recentTransactions = await db.transactions.findMany({
where: {
userId: transaction.userId,
timestamp: {
gte: new Date(Date.now() - 5 * 60 * 1000) // Last 5 minutes
}
}
})
if (recentTransactions.length > 5) {
anomalies.push(`High velocity: ${recentTransactions.length} transactions in 5 minutes`)
}
// Check 3: Impossible travel (location mismatch)
const lastTransaction = await db.transactions.findFirst({
where: { userId: transaction.userId },
orderBy: { timestamp: 'desc' }
})
if (lastTransaction) {
const timeDiff = transaction.timestamp.getTime() - lastTransaction.timestamp.getTime()
const distance = calculateDistance(lastTransaction.location, transaction.location)
const maxPossibleSpeed = 900 // km/h (commercial flight)
const requiredSpeed = distance / (timeDiff / 3600000) // km/h
if (requiredSpeed > maxPossibleSpeed) {
anomalies.push(
`Impossible travel: ${distance}km in ${timeDiff / 60000} minutes (${requiredSpeed.toFixed(0)} km/h required)`
)
}
}
// Check 4: New device from new location
const deviceHistory = await db.deviceHistory.findFirst({
where: {
userId: transaction.userId,
deviceId: transaction.deviceId
}
})
if (!deviceHistory) {
anomalies.push(`New device: ${transaction.deviceId}`)
}
// Check 5: Time-of-day anomaly
const hour = transaction.timestamp.getHours()
const userActivity = await getUserActivityPattern(transaction.userId)
if (userActivity.typicalHours.indexOf(hour) === -1) {
anomalies.push(`Unusual time: ${hour}:00 (typical: ${userActivity.typicalHours.join(', ')})`)
}
return {
isAnomalous: anomalies.length > 0,
anomalies,
riskScore: calculateRiskScore(anomalies)
}
}
async function getUserTransactionStats(userId: string) {
const result = await db.$queryRaw<[{ mean: number; stddev: number }]>`
SELECT
AVG(amount)::float as mean,
STDDEV(amount)::float as stddev
FROM transactions
WHERE user_id = ${userId}
AND timestamp > NOW() - INTERVAL '90 days'
`
return {
mean: result[0]?.mean || 0,
stdDev: result[0]?.stddev || 1
}
}
function calculateRiskScore(anomalies: string[]): number {
// Weight different anomaly types
const weights = {
'Unusual amount': 2,
'High velocity': 3,
'Impossible travel': 5,
'New device': 2,
'Unusual time': 1
}
return anomalies.reduce((score, anomaly) => {
const type = anomaly.split(':')[0]
return score + (weights[type] || 1)
}, 0)
}
Pattern 3: Data Lineage Tracking
Use case: Track data provenance and transformation history
// lib/forensics/lineage.ts
interface LineageNode {
id: string
datasetName: string
recordId: string
operation: string
timestamp: Date
sourceNodes: string[]
metadata: Record<string, any>
}
export class DataLineageTracker {
async trackTransformation(config: {
output: { dataset: string; recordId: string }
inputs: Array<{ dataset: string; recordId: string }>
operation: string
metadata?: Record<string, any>
}) {
const node: LineageNode = {
id: generateId(),
datasetName: config.output.dataset,
recordId: config.output.recordId,
operation: config.operation,
timestamp: new Date(),
sourceNodes: config.inputs.map(i => `${i.dataset}:${i.recordId}`),
metadata: config.metadata || {}
}
await db.dataLineage.create({ data: node })
return node
}
async getLineage(dataset: string, recordId: string): Promise<LineageNode[]> {
const visited = new Set<string>()
const lineage: LineageNode[] = []
async function traverse(ds: string, rid: string) {
const key = `${ds}:${rid}`
if (visited.has(key)) return
visited.add(key)
const node = await db.dataLineage.findFirst({
where: { datasetName: ds, recordId: rid }
})
if (!node) return
lineage.push(node)
// Recursively traverse source nodes
for (const sourceKey of node.sourceNodes) {
const [sourceDs, sourceRid] = sourceKey.split(':')
await traverse(sourceDs, sourceRid)
}
}
await traverse(dataset, recordId)
return lineage
}
async visualizeLineage(dataset: string, recordId: string): Promise<string> {
const lineage = await this.getLineage(dataset, recordId)
// Generate Mermaid diagram
let diagram = 'graph TD\n'
for (const node of lineage) {
const nodeId = `${node.datasetName}_${node.recordId}`
diagram += ` ${nodeId}["${node.datasetName}:${node.recordId}<br/>${node.operation}"]\\n`
for (const source of node.sourceNodes) {
const [ds, rid] = source.split(':')
const sourceId = `${ds}_${rid}`
diagram += ` ${sourceId} --> ${nodeId}\n`
}
}
return diagram
}
}
// Usage example
const tracker = new DataLineageTracker()
// Track ETL transformation
await tracker.trackTransformation({
output: { dataset: 'customers_enriched', recordId: 'c123' },
inputs: [
{ dataset: 'customers_raw', recordId: 'c123' },
{ dataset: 'geolocation', recordId: 'geo456' }
],
operation: 'enrichWithGeodata',
metadata: {
pipelineName: 'customer-enrichment',
version: '1.0.0'
}
})
Pattern 4: Fraud Detection Patterns
Use case: Identify fraudulent activities using pattern matching
// lib/forensics/fraud-detection.ts
interface FraudIndicator {
type: string
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
description: string
evidence: any
}
export async function detectFraud(userId: string): Promise<FraudIndicator[]> {
const indicators: FraudIndicator[] = []
// Pattern 1: Account takeover indicators
const recentPasswordChanges = await db.auditLog.count({
where: {
userId,
action: 'UPDATE',
tableName: 'users',
timestamp: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
}
})
if (recentPasswordChanges > 3) {
indicators.push({
type: 'ACCOUNT_TAKEOVER',
severity: 'HIGH',
description: 'Multiple password changes in 24 hours',
evidence: { count: recentPasswordChanges }
})
}
// Pattern 2: Structuring (smurfing)
const recentSmallTransactions = await db.transactions.findMany({
where: {
userId,
amount: { lt: 9999 }, // Just under reporting threshold
timestamp: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
}
})
if (recentSmallTransactions.length > 5) {
const total = recentSmallTransactions.reduce((sum, t) => sum + t.amount, 0)
indicators.push({
type: 'STRUCTURING',
severity: 'CRITICAL',
description: 'Multiple transactions just under reporting threshold',
evidence: {
count: recentSmallTransactions.length,
total,
averageAmount: total / recentSmallTransactions.length
}
})
}
// Pattern 3: Unusual beneficiary patterns
const beneficiaries = await db.transactions.groupBy({
by: ['beneficiaryId'],
where: {
userId,
timestamp: { gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }
},
_count: true
})
const newBeneficiaries = beneficiaries.filter(async b => {
const history = await db.transactions.count({
where: {
userId,
beneficiaryId: b.beneficiaryId,
timestamp: { lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
}
})
return history === 0
})
if (newBeneficiaries.length > 3) {
indicators.push({
type: 'UNUSUAL_BENEFICIARIES',
severity: 'MEDIUM',
description: 'Multiple new beneficiaries in short period',
evidence: { count: newBeneficiaries.length }
})
}
// Pattern 4: Round-tripping
const circularTransfers = await detectCircularTransfers(userId)
if (circularTransfers.length > 0) {
indicators.push({
type: 'ROUND_TRIPPING',
severity: 'CRITICAL',
description: 'Circular money flow detected',
evidence: { chains: circularTransfers }
})
}
return indicators
}
async function detectCircularTransfers(userId: string): Promise<string[][]> {
// Find chains where money flows back to original sender
const result = await db.$queryRaw<Array<{path: string[]}}>`
WITH RECURSIVE transfer_chain AS (
SELECT
sender_id,
recipient_id,
ARRAY[sender_id, recipient_id] as path,
1 as depth
FROM transfers
WHERE sender_id = ${userId}
AND timestamp > NOW() - INTERVAL '30 days'
UNION ALL
SELECT
tc.sender_id,
t.recipient_id,
tc.path || t.recipient_id,
tc.depth + 1
FROM transfer_chain tc
JOIN transfers t ON tc.recipient_id = t.sender_id
WHERE tc.depth < 5
AND NOT t.recipient_id = ANY(tc.path)
)
SELECT path
FROM transfer_chain
WHERE recipient_id = sender_id
`
return result.map(r => r.path)
}
Pattern 5: Breach Investigation
Use case: Investigate and analyze data breaches
// lib/forensics/breach-investigation.ts
interface BreachReport {
id: string
detectedAt: Date
affectedRecords: number
compromisedFields: string[]
attackVector: string
timeline: BreachEvent[]
impactAssessment: ImpactAssessment
}
interface BreachEvent {
timestamp: Date
event: string
details: any
}
export class BreachInvestigator {
async investigateBreach(suspiciousActivity: {
startTime: Date
endTime: Date
affectedTable: string
}): Promise<BreachReport> {
const timeline: BreachEvent[] = []
// 1. Identify initial access
const suspiciousLogins = await db.auditLog.findMany({
where: {
action: 'READ',
tableName: 'users',
timestamp: {
gte: suspiciousActivity.startTime,
lte: suspiciousActivity.endTime
}
},
orderBy: { timestamp: 'asc' }
})
timeline.push({
timestamp: suspiciousLogins[0]?.timestamp || suspiciousActivity.startTime,
event: 'INITIAL_ACCESS',
details: suspiciousLogins[0]
})
// 2. Identify privilege escalation
const privilegeChanges = await db.auditLog.findMany({
where: {
action: 'UPDATE',
tableName: 'users',
timestamp: {
gte: suspiciousActivity.startTime,
lte: suspiciousActivity.endTime
}
}
})
for (const change of privilegeChanges) {
if (change.newValue?.role !== change.oldValue?.role) {
timeline.push({
timestamp: change.timestamp,
event: 'PRIVILEGE_ESCALATION',
details: change
})
}
}
// 3. Identify data exfiltration
const dataAccess = await db.auditLog.findMany({
where: {
tableName: suspiciousActivity.affectedTable,
action: 'READ',
timestamp: {
gte: suspiciousActivity.startTime,
lte: suspiciousActivity.endTime
}
}
})
const massExfiltration = this.detectMassExfiltration(dataAccess)
if (massExfiltration) {
timeline.push({
timestamp: massExfiltration.startTime,
event: 'DATA_EXFILTRATION',
details: massExfiltration
})
}
// 4. Identify affected records
const affectedRecordIds = new Set(dataAccess.map(a => a.recordId))
const affectedRecords = affectedRecordIds.size
// 5. Determine compromised fields
const compromisedFields = this.identifyCompromisedFields(dataAccess)
// 6. Determine attack vector
const attackVector = await this.determineAttackVector(timeline)
// 7. Impact assessment
const impactAssessment = await this.assessImpact({
affectedRecords,
compromisedFields,
affectedTable: suspiciousActivity.affectedTable
})
return {
id: generateId(),
detectedAt: new Date(),
affectedRecords,
compromisedFields,
attackVector,
timeline,
impactAssessment
}
}
private detectMassExfiltration(accesses: any[]) {
// Group by user and time window
const byUser = groupBy(accesses, 'userId')
for (const [userId, userAccesses] of Object.entries(byUser)) {
const sorted = userAccesses.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())
// Check for rapid sequential access
if (sorted.length > 100) {
const duration =
sorted[sorted.length - 1].timestamp.getTime() - sorted[0].timestamp.getTime()
if (duration < 60000) {
// Less than 1 minute
return {
userId,
recordCount: sorted.length,
startTime: sorted[0].timestamp,
endTime: sorted[sorted.length - 1].timestamp,
duration
}
}
}
}
return null
}
private identifyCompromisedFields(accesses: any[]): string[] {
const fields = new Set<string>()
for (const access of accesses) {
if (access.metadata?.fields) {
access.metadata.fields.forEach(f => fields.add(f))
}
}
return Array.from(fields)
}
private async determineAttackVector(timeline: BreachEvent[]): Promise<string> {
// Analyze timeline to determine likely attack vector
const events = timeline.map(e => e.event)
if (events.includes('SQL_INJECTION')) {
return 'SQL Injection'
} else if (events.includes('PRIVILEGE_ESCALATION')) {
return 'Privilege Escalation'
} else if (events.includes('BRUTE_FORCE')) {
return 'Brute Force Attack'
} else {
return 'Unknown - Requires Manual Investigation'
}
}
private async assessImpact(config: {
affectedRecords: number
compromisedFields: string[]
affectedTable: string
}): Promise<ImpactAssessment> {
const sensitiveFields = ['ssn', 'password', 'creditCard', 'email', 'phone']
const hasSensitiveData = config.compromisedFields.some(f => sensitiveFields.includes(f))
return {
severity: hasSensitiveData ? 'CRITICAL' : 'HIGH',
affectedUsers: config.affectedRecords,
requiresNotification: true,
regulatoryImplications: hasSensitiveData ? ['GDPR', 'CCPA', 'State Breach Laws'] : [],
estimatedCost: this.estimateBreachCost(config.affectedRecords)
}
}
private estimateBreachCost(recordCount: number): number {
// Based on IBM Cost of Data Breach Report
const costPerRecord = 150 // USD
return recordCount * costPerRecord
}
}
interface ImpactAssessment {
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'
affectedUsers: number
requiresNotification: boolean
regulatoryImplications: string[]
estimatedCost: number
}
Pattern 6: Change Detection
Use case: Detect unauthorized database modifications
// lib/forensics/change-detection.ts
export class ChangeDetector {
async detectUnauthorizedChanges(tableName: string, timeWindow: number = 24) {
const changes = await db.auditLog.findMany({
where: {
tableName,
action: { in: ['UPDATE', 'DELETE'] },
timestamp: {
gte: new Date(Date.now() - timeWindow * 60 * 60 * 1000)
}
},
include: {
user: true
}
})
const suspicious: any[] = []
for (const change of changes) {
// Check 1: Change outside business hours
const hour = change.timestamp.getHours()
if (hour < 6 || hour > 22) {
suspicious.push({
...change,
reason: 'OUTSIDE_BUSINESS_HOURS',
severity: 'MEDIUM'
})
}
// Check 2: Bulk operations
const recentChanges = changes.filter(
c =>
c.userId === change.userId &&
Math.abs(c.timestamp.getTime() - change.timestamp.getTime()) < 60000
)
if (recentChanges.length > 50) {
suspicious.push({
...change,
reason: 'BULK_OPERATION',
severity: 'HIGH',
affectedRecords: recentChanges.length
})
}
// Check 3: Privilege mismatch
if (change.user.role !== 'ADMIN' && tableName === 'users') {
suspicious.push({
...change,
reason: 'INSUFFICIENT_PRIVILEGES',
severity: 'CRITICAL'
})
}
// Check 4: Unusual modifications
if (change.action === 'DELETE' && !change.metadata?.deletionReason) {
suspicious.push({
...change,
reason: 'UNDOCUMENTED_DELETION',
severity: 'HIGH'
})
}
}
return suspicious
}
async generateChangeReport(startDate: Date, endDate: Date) {
const changes = await db.auditLog.findMany({
where: {
timestamp: { gte: startDate, lte: endDate }
}
})
// Group by table and action
const summary = changes.reduce(
(acc, change) => {
const key = `${change.tableName}:${change.action}`
acc[key] = (acc[key] || 0) + 1
return acc
},
{} as Record<string, number>
)
// Group by user
const byUser = changes.reduce(
(acc, change) => {
acc[change.userId] = (acc[change.userId] || 0) + 1
return acc
},
{} as Record<string, number>
)
return {
totalChanges: changes.length,
byTableAndAction: summary,
byUser,
timeRange: { start: startDate, end: endDate }
}
}
}
Pattern 7: Forensic Recovery
Use case: Recover deleted or modified data
// lib/forensics/recovery.ts
export class ForensicRecovery {
async recoverDeletedRecord(tableName: string, recordId: string) {
// Find deletion event
const deletion = await db.auditLog.findFirst({
where: {
tableName,
recordId,
action: 'DELETE'
},
orderBy: { timestamp: 'desc' }
})
if (!deletion || !deletion.oldValue) {
throw new Error('No recovery data available')
}
// Verify record is still deleted
const exists = await db[tableName].findUnique({
where: { id: recordId }
})
if (exists) {
throw new Error('Record already exists')
}
// Restore from audit log
const restored = await db[tableName].create({
data: {
...deletion.oldValue,
restoredAt: new Date(),
restoredBy: getCurrentUser().id,
restoredFrom: deletion.id
}
})
// Log recovery
await createAuditLog({
userId: getCurrentUser().id,
action: 'CREATE',
tableName,
recordId,
newValue: restored,
metadata: {
recovery: true,
originalDeletionId: deletion.id
}
})
return restored
}
async reconstructHistory(tableName: string, recordId: string) {
const history = await db.auditLog.findMany({
where: { tableName, recordId },
orderBy: { timestamp: 'asc' }
})
const timeline = []
let currentState = null
for (const event of history) {
switch (event.action) {
case 'CREATE':
currentState = event.newValue
break
case 'UPDATE':
currentState = { ...currentState, ...event.newValue }
break
case 'DELETE':
currentState = null
break
}
timeline.push({
timestamp: event.timestamp,
action: event.action,
changes:
event.action === 'UPDATE' ? this.diffObjects(event.oldValue, event.newValue) : null,
state: structuredClone(currentState)
})
}
return timeline
}
private diffObjects(old: any, new_: any): any {
const changes = {}
for (const key of Object.keys(new_)) {
if (JSON.stringify(old[key]) !== JSON.stringify(new_[key])) {
changes[key] = { old: old[key], new: new_[key] }
}
}
return changes
}
async rollbackToTimestamp(tableName: string, recordId: string, timestamp: Date) {
const timeline = await this.reconstructHistory(tableName, recordId)
// Find state at target timestamp
const targetState = timeline.filter(t => t.timestamp <= timestamp).pop()?.state
if (!targetState) {
throw new Error('No state found at target timestamp')
}
// Update to target state
await db[tableName].update({
where: { id: recordId },
data: {
...targetState,
rolledBackAt: new Date(),
rolledBackBy: getCurrentUser().id,
rolledBackTo: timestamp
}
})
return targetState
}
}
Compliance Patterns
Pattern 8: GDPR Compliance Tools
// lib/forensics/gdpr-compliance.ts
export class GDPRCompliance {
async generateDataPortabilityReport(userId: string) {
// Collect all user data across tables
const userData = {
personal: await db.users.findUnique({ where: { id: userId } }),
orders: await db.orders.findMany({ where: { userId } }),
preferences: await db.userPreferences.findFirst({ where: { userId } }),
activityLog: await db.activityLog.findMany({
where: { userId },
orderBy: { timestamp: 'desc' },
take: 1000
})
}
return {
exportDate: new Date(),
userId,
data: userData,
format: 'JSON',
rightExercised: 'DATA_PORTABILITY'
}
}
async rightToBeForgotten(userId: string) {
// Log request
await db.gdprRequests.create({
data: {
userId,
requestType: 'ERASURE',
requestedAt: new Date(),
status: 'PROCESSING'
}
})
// Anonymize instead of delete for audit compliance
await db.users.update({
where: { id: userId },
data: {
email: `deleted-${userId}@example.com`,
name: 'Deleted User',
phone: null,
address: null,
deletedAt: new Date()
}
})
// Remove from third-party services
await this.removeFromThirdParties(userId)
// Update request status
await db.gdprRequests.update({
where: { userId },
data: {
status: 'COMPLETED',
completedAt: new Date()
}
})
}
async generateProcessingRegister() {
// GDPR Article 30 - Record of Processing Activities
return {
controller: {
name: 'Your Company Ltd',
contact: 'dpo@company.com',
representative: 'Data Protection Officer'
},
purposes: [
'Service delivery',
'Customer support',
'Marketing (with consent)',
'Legal compliance'
],
dataCategories: ['Identity data', 'Contact data', 'Financial data', 'Usage data'],
recipients: ['Payment processors', 'Email service providers', 'Analytics providers'],
retentionPeriods: {
activeUsers: '5 years after last activity',
deletedUsers: '1 year (anonymized)',
transactions: '7 years (legal requirement)'
},
securityMeasures: [
'Encryption at rest and in transit',
'Access control with MFA',
'Regular security audits',
'Audit logging'
]
}
}
}
Monitoring and Alerting
Pattern 9: Real-Time Threat Detection
// lib/forensics/threat-detection.ts
export class ThreatDetector {
async monitorForThreats() {
// Set up real-time monitoring
const threats = []
// Monitor 1: SQL Injection attempts
const sqlInjectionPatterns = [
/['";].*(--)|(;).*--/,
/\bUNION\b.*\bSELECT\b/i,
/\bOR\b.*['"].*=.*['"]/i
]
const recentQueries = await this.getRecentQueries()
for (const query of recentQueries) {
if (sqlInjectionPatterns.some(p => p.test(query.sql))) {
threats.push({
type: 'SQL_INJECTION',
severity: 'CRITICAL',
query: query.sql,
userId: query.userId,
timestamp: query.timestamp
})
}
}
// Monitor 2: Brute force attempts
const loginAttempts = await db.loginAttempts.groupBy({
by: ['ipAddress'],
where: {
success: false,
timestamp: { gte: new Date(Date.now() - 5 * 60 * 1000) }
},
_count: true
})
for (const attempt of loginAttempts) {
if (attempt._count > 10) {
threats.push({
type: 'BRUTE_FORCE',
severity: 'HIGH',
ipAddress: attempt.ipAddress,
attempts: attempt._count
})
}
}
// Monitor 3: Privilege escalation
const privilegeChanges = await db.auditLog.findMany({
where: {
tableName: 'users',
action: 'UPDATE',
timestamp: { gte: new Date(Date.now() - 60 * 60 * 1000) }
}
})
for (const change of privilegeChanges) {
if (change.newValue?.role === 'ADMIN' && change.oldValue?.role !== 'ADMIN') {
threats.push({
type: 'PRIVILEGE_ESCALATION',
severity: 'CRITICAL',
userId: change.userId,
targetUserId: change.recordId,
timestamp: change.timestamp
})
}
}
// Alert on critical threats
const criticalThreats = threats.filter(t => t.severity === 'CRITICAL')
if (criticalThreats.length > 0) {
await this.alertSecurityTeam(criticalThreats)
}
return threats
}
private async alertSecurityTeam(threats: any[]) {
// Send alerts via multiple channels
await Promise.all([
this.sendSlackAlert(threats),
this.sendEmailAlert(threats),
this.createPagerDutyIncident(threats)
])
}
}
When to Use Me
Perfect for:
- Security incident investigation
- Fraud detection and prevention
- Compliance auditing (GDPR, SOC2, HIPAA)
- Data breach response
- Forensic analysis of suspicious activity
- Data integrity verification
I'll help you:
- Build audit trail systems
- Detect anomalies and fraud
- Track data lineage
- Investigate breaches
- Ensure regulatory compliance
- Recover compromised data
What I'll Create
🔍 Audit Trail Systems
🚨 Anomaly Detection Engines
🔐 Fraud Detection Pipelines
📊 Data Lineage Tracking
🛡️ Breach Investigation Tools
⚖️ Compliance Reporting
🔄 Forensic Recovery Systems
Let's build secure, auditable, and compliant data systems!
FAQ
What does the forensic-data-engineer skill do?
Expert in data forensics, anomaly detection, audit trail analysis, fraud detection, and breach investigation
How do I install forensic-data-engineer?
Run npx skills add https://github.com/daffy0208/ai-dev-standards --skill forensic-data-engineer in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/forensic-data-engineer/ (personal) or .claude/skills/forensic-data-engineer/ (project).
Which AI agents can use forensic-data-engineer?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is forensic-data-engineer safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains forensic-data-engineer?
It is published by daffy0208 in daffy0208/ai-dev-standards. It has 178 recorded installs.
More from daffy0208/ai-dev-standards
animation-designer
daffy0208/ai-dev-standards
Expert in web animations, transitions, and motion design using Framer Motion and CSS
brand-designer
daffy0208/ai-dev-standards
Expert in brand identity, logo design, and visual brand systems
3d-visualizer
daffy0208/ai-dev-standards
Expert in Three.js, 3D graphics, and interactive 3D visualizations
data-visualizer
daffy0208/ai-dev-standards
Expert in creating charts, dashboards, and data visualizations using modern libraries
video-producer
daffy0208/ai-dev-standards
Open the skill to read its SKILL.md instructions.
knowledge base manager
daffy0208/ai-dev-standards
Open the skill to read its SKILL.md instructions.
Popular security skills
azure-compliance
microsoft/azure-skills
Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.
minimal-run-and-audit
lllllllama/rigorpilot-skills
Rigor Run skill for README-first deep learning repo reproduction. Use when the task is specifically to capture or normalize evidence from the selected smoke test or documented inference or evaluation command and write standardized reprooutputs/ files, including patch notes when repository files changed. Do not use for training execution, initial repo intake, generic environment setup, paper lookup, target selection, hidden scientific-meaning changes, or end-to-end orchestration by itself.
seo-audit
coreyhaines31/marketingskills
When the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," "indexing issues," "stuck on page 2," "striking distance keywords," "title tag rewrite," "low CTR," "local SEO," "Google Business Profile," or "map pack." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo. For adding structured data, see schema. For AI search optimization, see ai-seo.
firebase-security-rules-auditor
firebase/agent-skills
Audits Firebase (Firestore, Cloud Storage) security rules for vulnerabilities, privilege escalation, role bypasses, create vs update inconsistencies, resource exhaustion, type safety, size limits, and hasOnly ownership checks. Use when auditing/reviewing rules, running red-team rule assessments, or scoring against auditor checklists. Don't use for Firebase CLI (login, deploy), Auth, Crashlytics, Remote Config, or database queries.
audit
pbakaus/impeccable
Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
competitors
coreyhaines31/marketingskills
When the user wants to create competitor comparison or alternative pages for SEO and buyer-facing use. Also use when the user mentions 'alternative page,' 'vs page,' 'competitor comparison,' 'comparison page,' '[Product] vs [Product],' '[Product] alternative,' 'competitive landing pages,' 'how do we compare to X,' 'competitor teardown,' 'audit our competitor pages,' 'are our comparison pages out of date,' or 'competitive asset audit.' Use this for any content that positions your product against competitors. Covers four formats: singular alternative, plural alternatives, you vs competitor, and competitor vs competitor. For auditing existing claims (not researching competitors from scratch, which is competitor-profiling; not technical SEO on these pages, which is seo-audit), use the asset audit here. For internal battle cards and sales-specific competitor docs, see sales-enablement.
