Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
eac6390
feat: add SecurityTimelineView component with filters and chronologic…
RUKAYAT-CODER Jun 17, 2026
5bb1208
feat: add SecurityTimelineView styles
RUKAYAT-CODER Jun 17, 2026
52d5084
test: add SecurityTimelineView spec covering render, ordering, filter…
RUKAYAT-CODER Jun 17, 2026
0b1bb77
style: apply prettier formatting to SecurityTimelineView
RUKAYAT-CODER Jun 17, 2026
9b63e78
fix: use getAllByText for badges that appear in both dropdown and eve…
RUKAYAT-CODER Jun 17, 2026
298090f
feat(contracts): implement contract dependency tracker module (#151)
Topmatrixmor2014 Jun 17, 2026
24ab11c
fix(ci): add jest types to tsconfig and fix dockerfile build paths
Topmatrixmor2014 Jun 17, 2026
1d59e5c
Merge pull request #163 from Topmatrixmor2014/feat/contract-dependenc…
mijinummi Jun 17, 2026
f448aa9
feat(ai): add minimal ai provider abstraction and threat summarizatio…
Mkalbani Jun 17, 2026
c34a509
Merge pull request #164 from Mkalbani/feat/ai-framework
mijinummi Jun 17, 2026
3302465
Initialize Next.js Dashboard
ahmadogo Jun 17, 2026
821daa8
Initialize Next.js Dashboard
ahmadogo Jun 17, 2026
b7cdceb
fixed Lint errors
ahmadogo Jun 18, 2026
a9dcac3
fix: lint errors
ahmadogo Jun 18, 2026
54ae87e
fix: resolve lint errors and tsconfig issues
ahmadogo Jun 18, 2026
94689d0
fix: resolve lint errors and tsconfig issues
ahmadogo Jun 18, 2026
d28226d
feat: implement governance monitoring framework models and module
Jun 18, 2026
6cd7968
test: add governance tests
Jun 18, 2026
bba57e5
fix: ignore scripts in docker production install to fix husky error
Jun 18, 2026
cf7b45e
feat: add siem integration framework and multi-chain monitoring abstr…
Cybermaxi7 Jun 18, 2026
faa6808
Merge pull request #169 from SharifIbrahimDev/feat/governance-framewo…
mijinummi Jun 18, 2026
649a93e
chore: merge main into feat/siem-integration-and-chain-monitoring
Cybermaxi7 Jun 18, 2026
3004007
Merge pull request #171 from Cybermaxi7/feat/siem-integration-and-cha…
mijinummi Jun 18, 2026
d424d47
fix: pulled update from main branch and resolved conflicts
ahmadogo Jun 18, 2026
38d7a93
Merge pull request #165 from ahmadogo/feat/nextDashboard
mijinummi Jun 19, 2026
fd771c0
Merge branch 'feat/issue-156-security-timeline-view' of https://githu…
walexjnr Jun 19, 2026
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
10 changes: 8 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: ['tsconfig.json', 'apps/dashboard/tsconfig.json'],
project: ['tsconfig.json'],
tsconfigRootDir: __dirname,
sourceType: 'module',
},
Expand All @@ -12,7 +12,13 @@ module.exports = {
],
root: true,
env: { node: true },
ignorePatterns: ['.eslintrc.js', 'dist/**'],
// Exclude the Next.js dashboard app entirely — it uses its own tsconfig/eslint
// and its files are not included in the root tsconfig project references.
ignorePatterns: [
'.eslintrc.js',
'dist/**',
'apps/dashboard/**',
],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
dist/
node_modules/
coverage/
.next/
apps/dashboard/.next/
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
10 changes: 5 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ COPY . .
# Generate Prisma client
RUN npm run prisma:generate

# Build application (if you have a build script)
# RUN npm run build
# Build application
RUN npm run build:backend

# Runtime stage
FROM node:22-alpine
Expand All @@ -32,12 +32,12 @@ COPY package*.json ./
COPY prisma ./prisma/

# Install production dependencies only
RUN npm ci --only=production && \
RUN npm ci --only=production --ignore-scripts && \
npm cache clean --force

# Copy built application from builder
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/src ./src
COPY --from=builder /app/apps/backend/dist ./dist

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
Expand All @@ -55,4 +55,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
# Use dumb-init to handle signals properly
ENTRYPOINT ["dumb-init", "--"]

CMD ["node", "src/main.js"]
CMD ["node", "dist/apps/backend/src/main.js"]
15 changes: 14 additions & 1 deletion apps/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@ import { DatabaseModule } from '../../../database/database.module';
import { HealthModule } from './modules/health/health.module';
import { NotificationsModule } from './modules/notifications/notifications.module';
import { ReportingModule } from './modules/reporting/reporting.module';
import { DependencyTrackerModule } from './modules/contracts/dependencies/dependency-tracker.module';
import { GovernanceModule } from './modules/governance/governance.module';
import { SiemModule } from './integrations/siem/siem.module';
import { ChainsModule } from './modules/chains/chains.module';

@Module({
imports: [DatabaseModule, HealthModule, NotificationsModule, ReportingModule],
imports: [
DatabaseModule,
HealthModule,
NotificationsModule,
ReportingModule,
DependencyTrackerModule,
GovernanceModule,
SiemModule,
ChainsModule,
],
controllers: [AppController],
})
export class AppModule {}
23 changes: 23 additions & 0 deletions apps/backend/src/integrations/siem/dto/siem-config.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Configuration for the Splunk HTTP Event Collector (HEC) provider.
*/
export interface SplunkSiemConfig {
/** Full URL to the Splunk HEC endpoint (e.g. https://splunk.corp:8088/services/collector). */
hecUrl: string;
/** HEC token used for authentication. */
hecToken: string;
/** Splunk source type tag applied to every event (default: "sentinel:security"). */
sourceType?: string;
}

/**
* Configuration for the Elastic SIEM (ECS) provider.
*/
export interface ElasticSiemConfig {
/** Full URL to the Elasticsearch cluster (e.g. https://elastic.corp:9200). */
elasticUrl: string;
/** Elasticsearch API key for authentication. */
apiKey: string;
/** Target index for Sentinel events (default: "sentinel-events"). */
index?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Normalized security event forwarded to SIEM platforms.
* All provider adapters receive this common shape.
*/
export interface SiemEvent {
/** ISO-8601 timestamp of when the event occurred. */
timestamp: string;
/** Short machine-readable event type (e.g. "suspicious_transaction"). */
eventType: string;
/** Human-readable summary. */
title: string;
/** Full description with contextual detail. */
message: string;
severity: 'low' | 'medium' | 'high' | 'critical';
/** Source chain or system (e.g. "stellar", "ethereum", "internal"). */
source: string;
/** Arbitrary key-value pairs for provider-specific enrichment. */
metadata?: Record<string, unknown>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SiemEvent } from './siem-event.interface';

/**
* Contract every SIEM provider adapter must implement.
* Add new platforms (QRadar, Sentinel, etc.) by implementing this interface
* and registering the adapter in SiemModule — no changes to SiemService needed.
*/
export interface ISiemProvider {
/** Unique identifier for this provider (e.g. "splunk", "elastic"). */
readonly providerName: string;

/** Forward a normalized security event to the SIEM platform. */
forwardEvent(event: SiemEvent): Promise<void>;

/** Return true when the provider endpoint is reachable and configured. */
isHealthy(): Promise<boolean>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Injectable, Logger } from '@nestjs/common';
import axios from 'axios';
import { ISiemProvider } from '../interfaces/siem-provider.interface';
import { SiemEvent } from '../interfaces/siem-event.interface';
import { ElasticSiemConfig } from '../dto/siem-config.dto';

/**
* Forwards Sentinel security events to Elasticsearch using the ECS-aligned
* Bulk API. Events land in a configurable index for Elastic SIEM rules to consume.
*
* Environment variables:
* ELASTIC_URL — Elasticsearch cluster URL (e.g. https://elastic.corp:9200)
* ELASTIC_API_KEY — API key for authentication
* ELASTIC_INDEX — target index (default: "sentinel-events")
*/
@Injectable()
export class ElasticSiemProvider implements ISiemProvider {
readonly providerName = 'elastic';
private readonly logger = new Logger(ElasticSiemProvider.name);
private readonly index: string;

constructor(private readonly config: ElasticSiemConfig) {
this.index = config.index ?? 'sentinel-events';
}

async forwardEvent(event: SiemEvent): Promise<void> {
const doc = {
'@timestamp': event.timestamp,
'event.kind': 'alert',
'event.category': 'intrusion_detection',
'event.type': event.eventType,
'event.severity': this.severityToNumeric(event.severity),
message: event.message,
labels: {
title: event.title,
source: event.source,
severity: event.severity,
},
...event.metadata,
};

const body =
[JSON.stringify({ index: { _index: this.index } }), JSON.stringify(doc)].join('\n') + '\n';

try {
await axios.post(`${this.config.elasticUrl}/_bulk`, body, {
headers: {
Authorization: `ApiKey ${this.config.apiKey}`,
'Content-Type': 'application/x-ndjson',
},
});
this.logger.log(`Elastic: forwarded event "${event.eventType}"`);
} catch (error) {
const message = axios.isAxiosError(error)
? (error.response?.data?.error?.reason ?? error.message)
: String(error);
this.logger.error(`Elastic: forwardEvent failed: ${message}`);
throw new Error(`ElasticSiemProvider.forwardEvent failed: ${message}`);
}
}

async isHealthy(): Promise<boolean> {
try {
const response = await axios.get(`${this.config.elasticUrl}/_cluster/health`, {
headers: { Authorization: `ApiKey ${this.config.apiKey}` },
});
const status: string = response.data?.status ?? 'red';
return status !== 'red';
} catch (error) {
this.logger.warn(`Elastic health check failed: ${String(error)}`);
return false;
}
}

private severityToNumeric(severity: SiemEvent['severity']): number {
const map: Record<SiemEvent['severity'], number> = {
low: 25,
medium: 50,
high: 75,
critical: 100,
};
return map[severity];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Injectable, Logger } from '@nestjs/common';
import axios from 'axios';
import { ISiemProvider } from '../interfaces/siem-provider.interface';
import { SiemEvent } from '../interfaces/siem-event.interface';
import { SplunkSiemConfig } from '../dto/siem-config.dto';

/**
* Forwards Sentinel security events to Splunk via the HTTP Event Collector (HEC).
*
* Environment variables:
* SPLUNK_HEC_URL — HEC endpoint (e.g. https://splunk.corp:8088/services/collector)
* SPLUNK_HEC_TOKEN — HEC authentication token
* SPLUNK_SOURCE_TYPE — optional source type tag (default: "sentinel:security")
*/
@Injectable()
export class SplunkSiemProvider implements ISiemProvider {
readonly providerName = 'splunk';
private readonly logger = new Logger(SplunkSiemProvider.name);
private readonly sourceType: string;

constructor(private readonly config: SplunkSiemConfig) {
this.sourceType = config.sourceType ?? 'sentinel:security';
}

async forwardEvent(event: SiemEvent): Promise<void> {
const body = {
time: Math.floor(new Date(event.timestamp).getTime() / 1000),
sourcetype: this.sourceType,
event: {
event_type: event.eventType,
title: event.title,
message: event.message,
severity: event.severity,
source: event.source,
...event.metadata,
},
};

try {
await axios.post(this.config.hecUrl, body, {
headers: {
Authorization: `Splunk ${this.config.hecToken}`,
'Content-Type': 'application/json',
},
});
this.logger.log(`Splunk: forwarded event "${event.eventType}"`);
} catch (error) {
const message = axios.isAxiosError(error)
? (error.response?.data?.text ?? error.message)
: String(error);
this.logger.error(`Splunk: forwardEvent failed: ${message}`);
throw new Error(`SplunkSiemProvider.forwardEvent failed: ${message}`);
}
}

async isHealthy(): Promise<boolean> {
try {
await axios.get(this.config.hecUrl, {
headers: { Authorization: `Splunk ${this.config.hecToken}` },
validateStatus: status => status < 500,
});
return true;
} catch (error) {
this.logger.warn(`Splunk health check failed: ${String(error)}`);
return false;
}
}
}
52 changes: 52 additions & 0 deletions apps/backend/src/integrations/siem/siem.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Module } from '@nestjs/common';
import { SiemService } from './siem.service';
import { SplunkSiemProvider } from './providers/splunk.siem-provider';
import { ElasticSiemProvider } from './providers/elastic.siem-provider';
import { ISiemProvider } from './interfaces/siem-provider.interface';

/**
* SIEM Integration Module.
*
* Providers are registered conditionally based on environment variables so
* deployments without a SIEM platform incur no overhead.
*
* To add a new provider:
* 1. Implement ISiemProvider in providers/
* 2. Add its config env vars to .env.example
* 3. Register it in the SIEM_PROVIDERS factory below
*/
@Module({
providers: [
SiemService,
{
provide: 'SIEM_PROVIDERS',
useFactory: (): ISiemProvider[] => {
const providers: ISiemProvider[] = [];

if (process.env.SPLUNK_HEC_URL && process.env.SPLUNK_HEC_TOKEN) {

Check failure on line 26 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.

Check failure on line 26 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
providers.push(
new SplunkSiemProvider({
hecUrl: process.env.SPLUNK_HEC_URL,

Check failure on line 29 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
hecToken: process.env.SPLUNK_HEC_TOKEN,

Check failure on line 30 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
sourceType: process.env.SPLUNK_SOURCE_TYPE,

Check failure on line 31 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
}),
);
}

if (process.env.ELASTIC_URL && process.env.ELASTIC_API_KEY) {

Check failure on line 36 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.

Check failure on line 36 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
providers.push(
new ElasticSiemProvider({
elasticUrl: process.env.ELASTIC_URL,

Check failure on line 39 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
apiKey: process.env.ELASTIC_API_KEY,

Check failure on line 40 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
index: process.env.ELASTIC_INDEX,

Check failure on line 41 in apps/backend/src/integrations/siem/siem.module.ts

View workflow job for this annotation

GitHub Actions / TypeScript Type Checking

Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
}),
);
}

return providers;
},
},
],
exports: [SiemService],
})
export class SiemModule {}
Loading
Loading