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
48 changes: 39 additions & 9 deletions .agents/skills/update-org-data-integrations/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,34 @@ integration that references users:

#### Stable fields on Device

| Field | Description | Example |
| --------------------- | ----------------------------------- | ------------------- |
| `id` | Device UUID, used as `host.id` | `276e59a0-...` |
| `macAddress` | Stable MAC address (dash-separated) | `8a-d3-02-ed-99-a2` |
| `ipAddress` | Stable IPv4 address | `234.22.230.186` |
| `crowdstrikeAgentId` | CrowdStrike Falcon agent ID | `e045e02b...` |
| `crowdstrikeDeviceId` | CrowdStrike device ID | `efb573dc...` |
| `serialNumber` | Hardware serial number | `A1B2C3D4E5F6` |
| Field | Description | Example |
| --------------------- | ---------------------------------------------- | ------------------- |
| `id` | Device UUID, used as `host.id` | `276e59a0-...` |
| `macAddress` | Stable MAC address (dash-separated) | `8a-d3-02-ed-99-a2` |
| `ipAddress` | Stable IPv4 address | `234.22.230.186` |
| `crowdstrikeAgentId` | CrowdStrike Falcon agent ID | `e045e02b...` |
| `crowdstrikeDeviceId` | CrowdStrike device ID | `efb573dc...` |
| `serialNumber` | Hardware serial number | `A1B2C3D4E5F6` |
| `elasticAgentId` | Elastic Agent UUID for local workstation agent | `c3f1a9d2-...` |

#### Stable fields on Host

| Field | Description | Example |
| ---------------- | -------------------------------------- | ----------------------------- |
| `id` | Host UUID | `a2b3c4d5-...` |
| `name` | Server hostname | `api-server-prod-a1b2c3` |
| `elasticAgentId` | Elastic Agent UUID for the server host | `d4e5f6a7-...` |

#### CentralAgent on Organization

The `org.centralAgent` represents a single Elastic Agent deployed on a central fleet
collector server. All cloud/SaaS integrations (Okta, GWS, GitHub, etc.) share this agent
identity in their documents.

| Field | Description | Example |
| ------ | -------------------------------- | --------------------- |
| `id` | Stable UUID for the agent | `b7c8d9e0-...` |
| `name` | Hostname of the collector server | `fleet-collector-01` |

#### Correlation rules for ECS fields

Expand All @@ -209,6 +229,10 @@ When generating documents, map ECS fields to the stable Employee/Device values:
| `host.name` | `${employee.userName}-${device.platform}` for employee devices. |
| `host.mac` | Always `device.macAddress`. Convert separator as needed (dash for ECS/endpoint, colon for Jamf). **Never** call `faker.internet.mac()`. |
| `host.ip` | Always `device.ipAddress` as the primary IP. **Never** call `faker.internet.ipv4()` for host IPs. Random IPs are OK for `source.ip`, `destination.ip`, or `external_ip`. |
| `agent.id` | Local workstation: `device.elasticAgentId`. Server: `host.elasticAgentId`. Centralized cloud: `org.centralAgent.id`. Use `buildLocalAgent()`, `buildServerAgent()`, or `buildCentralAgent()` helpers from `BaseIntegration`. |
| `agent.name` | Local workstation: `${employee.userName}-${device.platform}` (same as `host.name`). Server: `host.name`. Centralized cloud: `org.centralAgent.name` (`fleet-collector-01`). |
| `agent.type` | `'endpoint'` for Elastic Defend, `'filebeat'` for all other integrations. |
| `agent.version` | Always `ELASTIC_AGENT_VERSION` (`'8.17.4'`), exported from `base_integration.ts`. |

#### When to extend the CorrelationMap

Expand Down Expand Up @@ -272,14 +296,20 @@ When examining a pipeline YAML, look for these processor types:
#### Document structure template

```typescript
// CORRECT: Raw pre-pipeline format
// CORRECT: Raw pre-pipeline format with agent metadata
return {
'@timestamp': timestamp,
agent: this.buildCentralAgent(org), // or buildLocalAgent(device, hostname) / buildServerAgent(host)
message: JSON.stringify(rawApiPayload),
data_stream: { namespace: 'default', type: 'logs', dataset: '<package>.<dataset>' },
} as IntegrationDocument;
```

Every document MUST include an `agent` field. Use the appropriate helper from `BaseIntegration`:
- **`buildCentralAgent(org)`** -- for cloud/SaaS integrations (Okta, GWS, GitHub, etc.)
- **`buildLocalAgent(device, hostname, agentType?)`** -- for per-workstation integrations (endpoint, crowdstrike, jamf_pro, island_browser, zscaler_zia)
- **`buildServerAgent(host)`** -- for per-server integrations (system)

#### What NOT to include

Do NOT pre-set any fields that the ingest pipeline derives:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BaseIntegration,
type IntegrationDocument,
type DataStreamConfig,
type AgentData,
} from './base_integration.ts';
import {
type Organization,
Expand Down Expand Up @@ -65,6 +66,7 @@ export class ActiveDirectoryIntegration extends BaseIntegration {
const documentsMap = new Map<string, IntegrationDocument[]>();
const documents: IntegrationDocument[] = [];
const timestamp = this.getTimestamp();
const centralAgent = this.buildCentralAgent(org);

const baseDn = `DC=${org.domain.replace('.com', '')},DC=com`;

Expand All @@ -73,7 +75,14 @@ export class ActiveDirectoryIntegration extends BaseIntegration {
const userDn = this.buildUserDn(employee, baseDn);
correlationMap.adDnToEmployee.set(userDn, employee);

const userDoc = this.createUserDocument(employee, org, timestamp, baseDn, userDn);
const userDoc = this.createUserDocument(
employee,
org,
timestamp,
baseDn,
userDn,
centralAgent,
);
documents.push(userDoc);
}

Expand All @@ -83,7 +92,14 @@ export class ActiveDirectoryIntegration extends BaseIntegration {
(d) => d.type === 'laptop' && d.platform === 'windows',
);
for (const device of windowsDevices) {
const computerDoc = this.createDeviceDocument(device, employee, org, timestamp, baseDn);
const computerDoc = this.createDeviceDocument(
device,
employee,
org,
timestamp,
baseDn,
centralAgent,
);
documents.push(computerDoc);
}
}
Expand All @@ -110,6 +126,7 @@ export class ActiveDirectoryIntegration extends BaseIntegration {
timestamp: string,
baseDn: string,
userDn: string,
centralAgent: AgentData,
): ActiveDirectoryDocument {
const whenCreated = faker.date.past({ years: 2 }).toISOString();
const whenChanged = faker.date.recent({ days: 30 }).toISOString();
Expand Down Expand Up @@ -191,6 +208,7 @@ export class ActiveDirectoryIntegration extends BaseIntegration {

return {
'@timestamp': timestamp,
agent: centralAgent,
activedirectory: {
id: userDn,
user: entry,
Expand Down Expand Up @@ -223,6 +241,7 @@ export class ActiveDirectoryIntegration extends BaseIntegration {
org: Organization,
timestamp: string,
baseDn: string,
centralAgent: AgentData,
): ActiveDirectoryDocument {
const whenCreated = faker.date.past({ years: 1 }).toISOString();
const whenChanged = faker.date.recent({ days: 14 }).toISOString();
Expand Down Expand Up @@ -265,6 +284,7 @@ export class ActiveDirectoryIntegration extends BaseIntegration {

return {
'@timestamp': timestamp,
agent: centralAgent,
activedirectory: {
id: computerDn,
device: entry,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BaseIntegration,
type IntegrationDocument,
type DataStreamConfig,
type AgentData,
} from './base_integration.ts';
import { type Organization, type Employee, type CorrelationMap } from '../types.ts';
import { faker } from '@faker-js/faker';
Expand Down Expand Up @@ -238,19 +239,24 @@ export class AtlassianBitbucketIntegration extends BaseIntegration {
): Map<string, IntegrationDocument[]> {
const documentsMap = new Map<string, IntegrationDocument[]>();
const documents: IntegrationDocument[] = [];
const centralAgent = this.buildCentralAgent(org);

for (const employee of org.employees) {
const eventCount = faker.number.int({ min: 2, max: 4 });
for (let i = 0; i < eventCount; i++) {
documents.push(this.createAuditDocument(employee, org));
documents.push(this.createAuditDocument(employee, org, centralAgent));
}
}

documentsMap.set(this.dataStreams[0].index, documents);
return documentsMap;
}

private createAuditDocument(employee: Employee, org: Organization): IntegrationDocument {
private createAuditDocument(
employee: Employee,
org: Organization,
centralAgent: AgentData,
): IntegrationDocument {
const action = faker.helpers.weightedArrayElement(
AUDIT_ACTIONS.map((a) => ({ value: a, weight: a.weight })),
);
Expand Down Expand Up @@ -287,6 +293,7 @@ export class AtlassianBitbucketIntegration extends BaseIntegration {

return {
'@timestamp': timestamp,
agent: centralAgent,
message: JSON.stringify(rawEvent),
data_stream: { namespace: 'default', type: 'logs', dataset: 'atlassian_bitbucket.audit' },
} as IntegrationDocument;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BaseIntegration,
type IntegrationDocument,
type DataStreamConfig,
type AgentData,
} from './base_integration.ts';
import { type Organization, type Employee, type CorrelationMap } from '../types.ts';
import { faker } from '@faker-js/faker';
Expand Down Expand Up @@ -210,19 +211,24 @@ export class AtlassianConfluenceIntegration extends BaseIntegration {
): Map<string, IntegrationDocument[]> {
const documentsMap = new Map<string, IntegrationDocument[]>();
const documents: IntegrationDocument[] = [];
const centralAgent = this.buildCentralAgent(org);

for (const employee of org.employees) {
const eventCount = faker.number.int({ min: 2, max: 4 });
for (let i = 0; i < eventCount; i++) {
documents.push(this.createAuditDocument(employee, org));
documents.push(this.createAuditDocument(employee, org, centralAgent));
}
}

documentsMap.set(this.dataStreams[0].index, documents);
return documentsMap;
}

private createAuditDocument(employee: Employee, org: Organization): IntegrationDocument {
private createAuditDocument(
employee: Employee,
org: Organization,
centralAgent: AgentData,
): IntegrationDocument {
const action = faker.helpers.weightedArrayElement(
AUDIT_ACTIONS.map((a) => ({ value: a, weight: a.weight })),
);
Expand Down Expand Up @@ -268,6 +274,7 @@ export class AtlassianConfluenceIntegration extends BaseIntegration {

return {
'@timestamp': timestamp,
agent: centralAgent,
message: JSON.stringify(rawEvent),
data_stream: { namespace: 'default', type: 'logs', dataset: 'atlassian_confluence.audit' },
} as IntegrationDocument;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BaseIntegration,
type IntegrationDocument,
type DataStreamConfig,
type AgentData,
} from './base_integration.ts';
import { type Organization, type Employee, type CorrelationMap } from '../types.ts';
import { faker } from '@faker-js/faker';
Expand Down Expand Up @@ -186,19 +187,24 @@ export class AtlassianJiraIntegration extends BaseIntegration {
): Map<string, IntegrationDocument[]> {
const documentsMap = new Map<string, IntegrationDocument[]>();
const documents: IntegrationDocument[] = [];
const centralAgent = this.buildCentralAgent(org);

for (const employee of org.employees) {
const eventCount = faker.number.int({ min: 2, max: 5 });
for (let i = 0; i < eventCount; i++) {
documents.push(this.createAuditDocument(employee, org));
documents.push(this.createAuditDocument(employee, org, centralAgent));
}
}

documentsMap.set(this.dataStreams[0].index, documents);
return documentsMap;
}

private createAuditDocument(employee: Employee, org: Organization): IntegrationDocument {
private createAuditDocument(
employee: Employee,
org: Organization,
centralAgent: AgentData,
): IntegrationDocument {
const action = faker.helpers.weightedArrayElement(
AUDIT_ACTIONS.map((a) => ({ value: a, weight: a.weight })),
);
Expand Down Expand Up @@ -233,6 +239,7 @@ export class AtlassianJiraIntegration extends BaseIntegration {

return {
'@timestamp': timestamp,
agent: centralAgent,
message: JSON.stringify(rawEvent),
data_stream: { namespace: 'default', type: 'logs', dataset: 'atlassian_jira.audit' },
} as IntegrationDocument;
Expand Down
11 changes: 9 additions & 2 deletions src/commands/org_data/integrations/auth0_integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BaseIntegration,
type IntegrationDocument,
type DataStreamConfig,
type AgentData,
} from './base_integration.ts';
import { type Organization, type Employee, type CorrelationMap } from '../types.ts';
import { faker } from '@faker-js/faker';
Expand Down Expand Up @@ -151,19 +152,24 @@ export class Auth0Integration extends BaseIntegration {
): Map<string, IntegrationDocument[]> {
const documentsMap = new Map<string, IntegrationDocument[]>();
const documents: IntegrationDocument[] = [];
const centralAgent = this.buildCentralAgent(org);

for (const employee of org.employees) {
const eventCount = faker.number.int({ min: 2, max: 5 });
for (let i = 0; i < eventCount; i++) {
documents.push(this.createLogDocument(employee, org));
documents.push(this.createLogDocument(employee, org, centralAgent));
}
}

documentsMap.set(this.dataStreams[0].index, documents);
return documentsMap;
}

private createLogDocument(employee: Employee, org: Organization): IntegrationDocument {
private createLogDocument(
employee: Employee,
org: Organization,
centralAgent: AgentData,
): IntegrationDocument {
const eventType = faker.helpers.weightedArrayElement(
AUTH0_EVENT_TYPES.map((e) => ({ value: e, weight: e.weight })),
);
Expand Down Expand Up @@ -200,6 +206,7 @@ export class Auth0Integration extends BaseIntegration {

return {
'@timestamp': timestamp,
agent: centralAgent,
json: { data: rawAuth0Data },
data_stream: { namespace: 'default', type: 'logs', dataset: 'auth0.logs' },
} as IntegrationDocument;
Expand Down
Loading
Loading