
Security Generate Security Sample Data
- 2.1k installs
- 546 repo stars
- Updated July 22, 2026
- elastic/agent-skills
security-generate-security-sample-data seeds Elastic Security with ECS events, attacks, and synthetic alerts.
About
The security-generate-security-sample-data skill populates Elastic Security dashboards, Alerts tab, and Attack Discovery with realistic sample data. It generates ECS-compliant events, multi-step attack scenarios, and synthetic alert documents for demos, POCs, detection rule testing, and dashboard validation. Requires Node.js 22+, network access to Kibana and Elasticsearch, and KIBANA_URL plus API key or username/password plus Elasticsearch URL or cloud ID credentials. Quick start path generates everything and opens Kibana with zero-friction defaults. Use when demoing Elastic Security, testing detection rules, or seeding POC environments without production traffic. ECS-compliant security events and multi-step attack scenarios Synthetic alerts for dashboards, Alerts tab, and Attack Discovery Requires Node 22+, Kibana and Elasticsearch credentials via env vars Zero-friction quick start generates data and opens Kibana Supports detection rule testing and security POC seeding security-generate-security-sample-data seeds Elastic Security with ECS events, attacks, and synthetic alerts Seeded dashboards and alerts with ECS-compliant attack scenarios for testing User demos Elastic Security,.
- ECS-compliant security events and multi-step attack scenarios.
- Synthetic alerts for dashboards, Alerts tab, and Attack Discovery.
- Requires Node 22+, Kibana and Elasticsearch credentials via env vars.
- Zero-friction quick start generates data and opens Kibana.
- Supports detection rule testing and security POC seeding.
Security Generate Security Sample Data by the numbers
- 2,125 all-time installs (skills.sh)
- +161 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #283 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
security-generate-security-sample-data capabilities & compatibility
- Capabilities
- ecs event generation · attack scenario synthesis · alert seeding · kibana demo setup · detection rule testing data
- Works with
- elasticsearch
- Use cases
- security audit · testing
npx skills add https://github.com/elastic/agent-skills --skill security-generate-security-sample-dataAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.1k |
|---|---|
| repo stars | ★ 546 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 22, 2026 |
| Repository | elastic/agent-skills ↗ |
How do I populate Elastic Security with realistic demo or test security data?
Generate ECS-compliant security events, attack scenarios, and synthetic alerts for Elastic Security demos and detection rule testing.
Who is it for?
Security engineers demoing Elastic or validating detection rules in POCs.
Skip if: Production incident response on live attacker traffic without synthetic scope.
When should I use this skill?
User demos Elastic Security, tests detection rules, or needs security sample data.
What you get
Seeded dashboards and alerts with ECS-compliant attack scenarios for testing.
- Synthetic security event logs
- Attack scenario datasets
- Test alert fixtures
By the numbers
- Supports 5 integration packages: system, endpoint, windows, aws, and okta
- Generates Windows logon events 4624 and 4625 plus process creation 4688
Files
Generate Security Sample Data
Generate ECS-compliant security events, multi-step attack scenarios, and synthetic alert documents that populate Elastic Security dashboards, the Alerts tab, and Attack Discovery.
Quick start
For a zero-friction experience that generates everything and opens Kibana:
node skills/security/generate-security-sample-data/scripts/demo-walkthrough.jsWorkflow
- [ ] Step 1: Set environment variables
- [ ] Step 2: Generate sample data
- [ ] Step 3: Explore in Kibana
- [ ] Step 4: Clean up when doneStep 1: Set environment variables
export ELASTICSEARCH_URL="https://your-project.es.region.aws.elastic.cloud"
export ELASTICSEARCH_USERNAME="admin"
export ELASTICSEARCH_PASSWORD="your-password"
export KIBANA_URL="https://your-project.kb.region.aws.elastic.cloud"Step 2: Generate sample data
Generate everything at once
node skills/security/generate-security-sample-data/scripts/sample-data.js \
system endpoint okta aws windows --scenarios --alertsGenerate only events
node skills/security/generate-security-sample-data/scripts/sample-data.js \
system endpoint --count 100Generate only attack scenarios
node skills/security/generate-security-sample-data/scripts/sample-data.js --scenariosGenerate only synthetic alerts
node skills/security/generate-security-sample-data/scripts/sample-data.js --alertsStep 3: Explore in Kibana
After generating data, direct the user to these pages:
- Security > Alerts — synthetic alerts with MITRE ATT&CK mappings
- Security > Attack Discovery — requires an LLM connector to analyze alerts
- Security > Hosts — host activity from sample events
- Security > Overview — summary of all security data
- Discover — raw events across all data streams
Step 4: Clean up when done
node skills/security/generate-security-sample-data/scripts/sample-data.js --cleanupWhat gets generated
Sample data spans 5 packages (system, endpoint, windows, aws, okta) and 4 focused attack scenarios covering the most common demo themes: Windows credential theft, AWS cloud privilege escalation, Okta identity takeover, and a full ransomware kill chain. Synthetic alert documents are indexed into .alerts-security.alerts-default with MITRE ATT&CK mappings, severity levels, and risk scores.
All events use RFC 5737 / RFC 2606 safe addresses. For full tables of packages, scenarios, and alerts see references/sample-data-reference.md.
Continuous mode
Stream events to simulate a live environment:
node skills/security/generate-security-sample-data/scripts/sample-data.js \
--continuous --interval 15Every 5th batch includes an attack scenario; every 10th batch adds synthetic alerts. Press Ctrl+C to stop.
Tool reference
sample-data.js
| Flag | Description |
|---|---|
--count, -n | Events per package (default: 50) |
--scenarios | Run all attack simulation scenarios |
--scenario NAME | Run a specific scenario |
--alerts | Generate synthetic alert documents |
--cleanup | Remove all sample data and alerts |
--continuous | Stream live events (Ctrl+C to stop) |
--interval N | Seconds between continuous batches (default: 30) |
--json, -j | Output results as JSON |
--yes, -y | Skip confirmation prompts |
demo-walkthrough.js
Zero-friction runner that generates everything and opens Kibana.
| Flag | Description |
|---|---|
--cleanup | Remove all sample data, alerts, case |
--continuous | Generate then stream live events |
--count N | Events per package (default: 50) |
--interval N | Seconds between batches (default: 30) |
Examples
Quick demo for a stakeholder
"Set up a demo environment so I can show Attack Discovery to my VP."
node skills/security/generate-security-sample-data/scripts/demo-walkthrough.jsTargeted scenario testing
"Generate only the ransomware attack chain to test our detection rules."
node skills/security/generate-security-sample-data/scripts/sample-data.js \
--scenario ransomwareChain --alertsSimulating a live SOC
"Keep generating events so the dashboards stay active during the demo."
node skills/security/generate-security-sample-data/scripts/demo-walkthrough.js --continuousCleaning up after a demo
"Remove all sample data from my project."
node skills/security/generate-security-sample-data/scripts/sample-data.js --cleanupGuidelines
- All generated documents are tagged with
tags: ["elastic-security-sample-data"]for safe cleanup. The cleanup command
only deletes documents with this marker.
- If marker fields are not indexed in a data stream, cleanup falls back to scanning
_source.tagsfor matching sample
documents from the last 14 days.
- Synthetic alerts are indexed directly into
.alerts-security.alerts-default— they do not require detection rules to
be installed or enabled.
- Attack Discovery requires an LLM connector (OpenAI, Anthropic, Google Gemini, or similar) configured in Kibana under
Stack Management > Connectors. The "Complete" project tier unlocks the feature, but the connector must be set up separately.
- Use the
case-managementskill for creating investigation cases from alerts.
Production use
- Do not run against production clusters unless you intend to inject synthetic data alongside real alerts. Sample
events and alerts are tagged for cleanup but will appear in dashboards, the Alerts tab, and Attack Discovery alongside real data.
- All write operations (
generate,--cleanup,--continuous) prompt for confirmation. Pass--yesor-yto skip
when called by an agent.
--cleanuprunsdeleteByQueryacross all sample data indices — verify environment variables point to the intended
cluster before running.
--continuousmode indexes events indefinitely until manually stopped with Ctrl+C.
Environment variables
| Variable | Required | Description |
|---|---|---|
ELASTICSEARCH_URL | Yes | Elasticsearch URL |
ELASTICSEARCH_API_KEY | Yes\* | Elasticsearch API key |
ELASTICSEARCH_USERNAME | Yes\* | Elasticsearch username (alternative) |
ELASTICSEARCH_PASSWORD | Yes\* | Elasticsearch password (alternative) |
KIBANA_URL | No | Kibana URL (for case creation and links) |
KIBANA_USERNAME | No | Kibana username (if using Kibana features) |
KIBANA_PASSWORD | No | Kibana password (if using Kibana features) |
\*Either API key or username/password is required for Elasticsearch.
Sample Data Reference
Detailed tables for events, attack scenarios, and synthetic alerts generated by sample-data.js.
Supported packages
| Package | Event types generated |
|---|---|
system | SSH auth success/failure, process start, syslog |
endpoint | Process exec, network connection, file creation |
windows | Logon 4624/4625, process creation 4688 |
aws | CloudTrail: ConsoleLogin, AssumeRole, API calls |
okta | Session start, MFA auth, account lock/password change |
All events use RFC 5737 / RFC 2606 safe addresses to avoid triggering real security alerts.
Attack simulation scenarios
Correlated, multi-step event sequences that mirror real-world attack chains. Events are indexed into standard data streams with field values that resemble patterns targeted by prebuilt detection rules, providing realistic log activity for dashboards and Discover.
| Scenario | Attack chain | Rules targeted |
|---|---|---|
windowsCredentialAccess | Failed logins, success, encoded PowerShell, scheduled task, LSASS dump, SAM export | Credential Dumping, Suspicious PowerShell, Scheduled Task Persistence |
awsIAMEscalation | Console login without MFA, create IAM user, attach admin policy, create access keys, turn off trail | AWS Console Login Without MFA, IAM Privilege Escalation, CloudTrail Logging Off |
oktaAccountTakeover | 10 failed logins, 5 MFA push rejections, successful login, admin role grant, policy modification | Okta Brute Force, MFA Bombing, Admin Role Assigned |
ransomwareChain | Phishing macro, encoded PowerShell, LSASS dump, C2 beacon, shadow copy deletion, file encryption, ransom note | Macro Execution, LSASS Dump, C2 Beacon, Volume Shadow Copy Deletion, Ransomware |
Synthetic alert scenarios
Alert documents indexed directly into .alerts-security.alerts-default with MITRE ATT&CK mappings, risk scores, and severity levels. These power Attack Discovery without waiting for detection rules to fire.
| Alert scenario | Alerts generated | Severities |
|---|---|---|
credentialAccessAlerts | Windows brute force, encoded PowerShell, scheduled task, LSASS dump, SAM export | high, high, medium, critical, critical |
awsEscalationAlerts | Console login without MFA, IAM user created, admin policy attached, CloudTrail turned off | high, medium, critical, critical |
oktaTakeoverAlerts | Brute force, MFA bombing, admin role grant, policy modification | high, high, critical, high |
ransomwareAlerts | Macro execution, C2 beacon, LSASS dump, shadow copy deletion, file encryption, ransom note | high, critical, critical, critical, critical, critical |
#!/usr/bin/env node
/**
* Zero-friction sample data runner.
*
* Usage:
* node demo-walkthrough.js # Generate everything once
* node demo-walkthrough.js --continuous # Keep streaming events
* node demo-walkthrough.js --cleanup # Remove all sample data
*/
import { exec } from "node:child_process";
import { testConnection as testEsConnection } from "./es-client.js";
import { testConnection as testKibanaConnection } from "./kibana-client.js";
import { kibanaGet, kibanaPost } from "./kibana-client.js";
import {
generateAndIndex,
runAttackScenarios,
generateAlerts,
generateCase,
cleanup,
runContinuous,
SUPPORTED_PACKAGES,
} from "./sample-data.js";
function openBrowser(url) {
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
exec(`${cmd} "${url}"`);
}
function printKibanaLinks() {
const kibanaUrl = process.env.KIBANA_URL;
if (!kibanaUrl) return;
console.log("\n Explore in Kibana:\n");
console.log(` Alerts: ${kibanaUrl}/app/security/alerts`);
console.log(` Attack Discovery: ${kibanaUrl}/app/security/attack_discovery`);
console.log(` Cases: ${kibanaUrl}/app/security/cases`);
console.log(` Hosts: ${kibanaUrl}/app/security/hosts`);
console.log(` Overview: ${kibanaUrl}/app/security/overview`);
}
async function checkEnv() {
const hasES = process.env.ELASTICSEARCH_URL || process.env.ELASTICSEARCH_CLOUD_ID;
const hasKibana = process.env.KIBANA_URL;
const hasAuth =
process.env.ELASTICSEARCH_API_KEY || (process.env.ELASTICSEARCH_USERNAME && process.env.ELASTICSEARCH_PASSWORD);
if (!hasES || !hasAuth) {
console.error("Missing environment variables. Set these before running:\n");
console.error(" export ELASTICSEARCH_URL='https://your-project.es.region.aws.elastic.cloud'");
console.error(" export KIBANA_URL='https://your-project.kb.region.aws.elastic.cloud'");
console.error(" export ELASTICSEARCH_USERNAME='admin'");
console.error(" export ELASTICSEARCH_PASSWORD='your-password'");
process.exit(1);
}
console.log("Checking connections...");
const esOk = await testEsConnection();
if (!esOk) {
console.error("Elasticsearch connection failed. Check your credentials.");
process.exit(1);
}
if (hasKibana) {
const kibanaOk = await testKibanaConnection();
if (!kibanaOk) console.log(" Kibana connection failed — case won't be created, but events/alerts will work.");
}
}
async function generate(count) {
console.log(`\n--- Sample Events (${count} per package) ---\n`);
const { totalIndexed, totalErrors } = await generateAndIndex([...SUPPORTED_PACKAGES], count);
console.log(` ${totalIndexed} events indexed, ${totalErrors} errors`);
console.log("\n--- Attack Scenarios ---\n");
const scenarioResult = await runAttackScenarios([]);
console.log(` ${scenarioResult.totalIndexed} scenario events indexed`);
console.log("\n--- Synthetic Alerts ---\n");
const alertResult = await generateAlerts([]);
console.log(` ${alertResult.totalIndexed} alerts indexed`);
if (process.env.KIBANA_URL) {
console.log("\n--- Investigation Case ---\n");
try {
await generateCase(kibanaPost, kibanaGet, alertResult.alertRefs || []);
} catch (e) {
console.error(` Case skipped: ${e.message}`);
}
}
const total = totalIndexed + scenarioResult.totalIndexed + alertResult.totalIndexed;
console.log(`\nDone: ${total} total documents indexed.`);
printKibanaLinks();
if (process.env.KIBANA_URL) {
openBrowser(`${process.env.KIBANA_URL}/app/security/alerts`);
console.log("\n Opened Security > Alerts in your browser.");
}
}
async function main() {
const argv = process.argv.slice(2);
const doCleanup = argv.includes("--cleanup");
const continuous = argv.includes("--continuous");
const countArg = argv.find((_, i) => argv[i - 1] === "--count" || argv[i - 1] === "-n");
const count = parseInt(countArg, 10) || 50;
const intervalArg = argv.find((_, i) => argv[i - 1] === "--interval");
const interval = parseInt(intervalArg, 10) || 30;
console.log("");
console.log(" Elastic Security — Sample Data Generator");
console.log(" =========================================\n");
await checkEnv();
if (doCleanup) {
console.log("");
await cleanup();
console.log("\nAll sample data removed.");
return;
}
if (continuous) {
console.log(`\nStarting continuous mode (every ${interval}s, Ctrl+C to stop)...\n`);
await generate(count);
console.log("\nSwitching to continuous stream...\n");
await runContinuous(interval, 10);
return;
}
await generate(count);
}
main().catch((e) => {
console.error(e.message || e);
process.exit(1);
});
/**
* Elasticsearch client factory for SOC skills.
* Supports Cloud ID, direct URL, API key, and basic auth.
*/
import { Client } from "@elastic/elasticsearch";
try {
process.loadEnvFile();
} catch {}
/**
* Create and return an Elasticsearch client using environment variables.
*/
export function createClient() {
const cloudId = process.env.ELASTICSEARCH_CLOUD_ID;
const apiKey = process.env.ELASTICSEARCH_API_KEY;
const url = process.env.ELASTICSEARCH_URL;
const username = process.env.ELASTICSEARCH_USERNAME;
const password = process.env.ELASTICSEARCH_PASSWORD;
const insecure = process.env.ELASTICSEARCH_INSECURE === "true";
const config = {};
if (cloudId) {
config.cloud = { id: cloudId };
} else if (url) {
config.node = url;
} else {
console.error("Error: No Elasticsearch connection configured.");
console.error("Set ELASTICSEARCH_CLOUD_ID or ELASTICSEARCH_URL environment variable.");
process.exit(1);
}
if (apiKey) {
config.auth = { apiKey };
} else if (username && password) {
config.auth = { username, password };
} else if (username || password) {
console.error("Error: Both ELASTICSEARCH_USERNAME and ELASTICSEARCH_PASSWORD must be set for basic auth.");
process.exit(1);
}
if (insecure) {
config.tls = { rejectUnauthorized: false };
}
config.headers = { "User-Agent": "elastic-agentic" };
return new Client(config);
}
export async function testConnection() {
try {
const client = createClient();
const info = await client.info();
console.log(`Connected to Elasticsearch cluster: ${info.cluster_name}`);
console.log(`Version: ${info.version.number}`);
await client.close();
return true;
} catch (error) {
console.error(`Connection failed: ${error.message}`);
return false;
}
}
/**
* Lightweight HTTP client for the Kibana REST API.
* Uses native fetch() with auth, retry on 429, and space support.
*/
try {
process.loadEnvFile();
} catch {}
const RETRY_DELAYS = [5, 10, 20];
export function getKibanaConfig() {
const url = process.env.KIBANA_URL;
const apiKey = process.env.KIBANA_API_KEY;
const username = process.env.KIBANA_USERNAME || process.env.ELASTICSEARCH_USERNAME;
const password = process.env.KIBANA_PASSWORD || process.env.ELASTICSEARCH_PASSWORD;
const spaceId = process.env.KIBANA_SPACE_ID;
const insecure = process.env.KIBANA_INSECURE === "true";
if (!url) {
console.error("Error: No Kibana connection configured.");
console.error("Set KIBANA_URL environment variable.");
process.exit(1);
}
if (!apiKey && !username && !password && process.env.KIBANA_NO_AUTH !== "true") {
console.error("Error: No Kibana authentication configured.");
console.error("Set KIBANA_API_KEY or KIBANA_USERNAME + KIBANA_PASSWORD.");
console.error("Or set KIBANA_NO_AUTH=true for clusters with security disabled.");
process.exit(1);
}
if (!apiKey && ((username && !password) || (!username && password))) {
console.error("Error: Both username and password must be set for basic auth.");
console.error("Set KIBANA_USERNAME + KIBANA_PASSWORD (or ELASTICSEARCH_USERNAME + ELASTICSEARCH_PASSWORD).");
process.exit(1);
}
return { url, apiKey, username, password, spaceId, insecure };
}
function getHeaders(config) {
const headers = {
"Content-Type": "application/json",
"kbn-xsrf": "true",
"User-Agent": "elastic-agentic",
};
if (config.apiKey) {
headers["Authorization"] = `ApiKey ${config.apiKey}`;
} else if (config.username && config.password) {
const auth = Buffer.from(`${config.username}:${config.password}`).toString("base64");
headers["Authorization"] = `Basic ${auth}`;
}
return headers;
}
function getBasePath(config, space) {
let basePath = config.url.replace(/\/$/, "");
const effectiveSpace = space || config.spaceId;
if (effectiveSpace && effectiveSpace !== "default") {
basePath += `/s/${effectiveSpace}`;
}
return basePath;
}
/**
* Make an HTTP request to the Kibana API with automatic 429 retry.
*
* @param {string} path - API path (e.g. "/api/cases")
* @param {object} [options] - fetch options (method, body, headers, params)
* @param {string} [options.space] - Override Kibana space for this request
* @returns {{ success: boolean, data?: any, status?: number, error?: string }}
*/
export async function kibanaFetch(path, options = {}) {
const config = getKibanaConfig();
const { space, params, ...fetchOpts } = options;
const basePath = getBasePath(config, space);
let url = `${basePath}${path}`;
if (params) {
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== null) {
if (Array.isArray(value)) {
for (const v of value) searchParams.append(key, v);
} else {
searchParams.append(key, String(value));
}
}
}
const qs = searchParams.toString();
if (qs) url += `?${qs}`;
}
const requestOptions = {
...fetchOpts,
headers: {
...getHeaders(config),
...fetchOpts.headers,
},
};
if (config.insecure) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
}
for (let attempt = 0; attempt <= RETRY_DELAYS.length; attempt++) {
try {
const response = await fetch(url, requestOptions);
if (response.status === 429 && attempt < RETRY_DELAYS.length) {
const delay = RETRY_DELAYS[attempt];
console.error(`Rate limited, retrying in ${delay}s (attempt ${attempt + 1}/${RETRY_DELAYS.length + 1})...`);
await new Promise((r) => setTimeout(r, delay * 1000));
continue;
}
const contentType = response.headers.get("content-type");
let data;
if (contentType && contentType.includes("application/json")) {
data = await response.json();
} else {
data = await response.text();
}
if (!response.ok) {
return {
success: false,
status: response.status,
error: data?.message || data?.error || `HTTP ${response.status}`,
details: data,
};
}
return { success: true, data };
} catch (error) {
if (attempt < RETRY_DELAYS.length && error.message?.includes("429")) {
const delay = RETRY_DELAYS[attempt];
console.error(`Rate limited, retrying in ${delay}s...`);
await new Promise((r) => setTimeout(r, delay * 1000));
continue;
}
return { success: false, error: error.message, details: error };
}
}
}
/**
* Convenience wrappers matching the Python KibanaClient interface.
* These throw on HTTP errors (matching the old behavior where scripts
* relied on exceptions for error handling).
*/
export async function kibanaGet(path, params, space) {
const result = await kibanaFetch(path, { method: "GET", params, space });
if (!result.success) throw new Error(result.error || `HTTP ${result.status}`);
return result.data;
}
export async function kibanaPost(path, body, space) {
const result = await kibanaFetch(path, {
method: "POST",
body: body !== undefined ? JSON.stringify(body) : undefined,
space,
});
if (!result.success) throw new Error(result.error || `HTTP ${result.status}`);
return result.data;
}
export async function kibanaPatch(path, body, space) {
const result = await kibanaFetch(path, {
method: "PATCH",
body: body !== undefined ? JSON.stringify(body) : undefined,
space,
});
if (!result.success) throw new Error(result.error || `HTTP ${result.status}`);
return result.data;
}
export async function kibanaPut(path, body, space) {
const result = await kibanaFetch(path, {
method: "PUT",
body: body !== undefined ? JSON.stringify(body) : undefined,
space,
});
if (!result.success) throw new Error(result.error || `HTTP ${result.status}`);
return result.data;
}
export async function kibanaDelete(path, space) {
const result = await kibanaFetch(path, { method: "DELETE", space });
if (!result.success) throw new Error(result.error || `HTTP ${result.status}`);
return result.data;
}
export async function testConnection(space) {
try {
const status = await kibanaGet("/api/status", undefined, space);
const version = status?.version;
const versionStr = typeof version === "object" ? version?.number : version;
console.log(`Connected to Kibana: ${status?.name || "unknown"}`);
console.log(`Version: ${versionStr || "unknown"}`);
return true;
} catch (error) {
console.error(`Connection failed: ${error.message}`);
return false;
}
}
Related skills
Forks & variants (1)
Security Generate Security Sample Data has 1 known copy in the catalog totaling 2 installs. They canonicalize to this original listing.
- elastic - 2 installs
How it compares
Use this skill over generic mock-data generators when testing Elastic Security detection rules that expect integration-specific event schemas.
FAQ
What credentials are required?
KIBANA_URL and Kibana auth plus Elasticsearch URL or cloud ID with Elasticsearch auth.
What Node version is required?
Node.js 22 or newer per skill compatibility notes.
What data formats are generated?
ECS-compliant events, attack scenarios, and synthetic alert documents.
Is Security Generate Security Sample Data safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.