Add unlabelled event detection, stub parser quality, Sync All, and modern UI redesign

Key changes:
- Unlabelled event banner: shows count only after Sample Events is clicked; uses broad SDL filter expression; time window synced to sync-days dropdown
- Parser Quality: new "Attributes Missing" subsection listing all parsers without dataSource.name regardless of event volume
- Coverage map: filter buttons (All / Complete Parser / Attributes Missing); stat card renamed to "Incomplete Parser"; stub count excluded from sync when no active sources
- Sync All button: runs SDL parser sync → library sync → live sources sync in sequence
- Reset now clears ActiveSource table and resets unlabelled count cache
- run_powerquery: configurable max_count param (default 1000, 50M for count queries)
- _DS_NAME_RE: supports both quoted and unquoted dataSource.name keys in parser files
- Full modern UI redesign: slate palette, gradient cards, ring borders, pill nav, colored stat accents
- Updated 7 tracked parser files synced from SDL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mick
2026-05-22 10:00:21 -04:00
parent 0013adbe7e
commit c5a4f796a0
15 changed files with 3498 additions and 469 deletions
+2 -1
View File
@@ -1,5 +1,5 @@
import os
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, Text
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, Text, Boolean
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import declarative_base, sessionmaker
from datetime import datetime
@@ -37,6 +37,7 @@ class ActiveSource(Base):
event_count = Column(Integer, default=0)
synced_at = Column(DateTime, default=datetime.utcnow)
parser_detected = Column(Integer, default=0) # >0 means parsed events seen in data lake
unlabelled = Column(Boolean, default=False) # True = events had no dataSource.name
class IngestSnapshot(Base):
+3
View File
@@ -11,6 +11,9 @@ with engine.connect() as _conn:
_conn.execute(text(
"ALTER TABLE active_sources ADD COLUMN IF NOT EXISTS parser_detected INTEGER DEFAULT 0"
))
_conn.execute(text(
"ALTER TABLE active_sources ADD COLUMN IF NOT EXISTS unlabelled BOOLEAN DEFAULT FALSE"
))
_conn.commit()
app = FastAPI(title="SIEM Toolkit", version="1.0.0")
+173 -16
View File
@@ -207,16 +207,109 @@ async def upload_sigma(files: list[UploadFile] = File(...), db: Session = Depend
return {"loaded": len(loaded), "rules": loaded}
def _fetch_parsers_from_console(parsers_dir: str) -> dict:
"""
Fetch every parser under /logParsers/ from the SDL console and write them
to parsers_dir. Uses SDL_CONFIG_READ_KEY (needs 'Manage config files' permission)
and SDL_XDR_URL from the environment.
Returns {"fetched": N, "failed": [...], "skipped": reason_or_None}
"""
import urllib.request, urllib.error, json as _json, os as _os
# Read live from .env file so Settings-page saves are picked up without restart
def _env_val(key: str) -> str:
val = _os.environ.get(key, "")
if not val:
env_path = _os.environ.get("ENV_FILE_PATH", "/app/.env")
try:
for line in open(env_path).read().splitlines():
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, _, v = line.partition("=")
if k.strip() == key:
val = v.strip()
break
except Exception:
pass
return val
config_key = _env_val("SDL_CONFIG_READ_KEY")
base_url = _env_val("SDL_XDR_URL").rstrip("/")
if not config_key:
return {"fetched": 0, "failed": [], "skipped": "SDL_CONFIG_READ_KEY not set"}
if not base_url:
return {"fetched": 0, "failed": [], "skipped": "SDL_XDR_URL not set"}
def _post(path: str, params: dict) -> dict:
url = f"{base_url}{path}"
body = _json.dumps({**params, "token": config_key}).encode()
req = urllib.request.Request(url, data=body, headers={
"Authorization": f"Bearer {config_key}",
"Content-Type": "application/json",
})
try:
with urllib.request.urlopen(req, timeout=30) as r:
return _json.loads(r.read())
except urllib.error.HTTPError as e:
err_body = e.read().decode(errors="replace")[:300]
raise RuntimeError(f"HTTP {e.code} {path}: {err_body}")
# List all parser paths
res = _post("/api/listFiles", {"pathPrefix": "/logParsers/"})
# Support multiple response shapes: {"paths": [...]} or {"files": [...]}
raw_paths = res.get("paths") or res.get("files") or []
# Each element may be a plain string or a dict with a "path"/"name" key
paths = []
for p in raw_paths:
if isinstance(p, dict):
p = p.get("path") or p.get("name") or ""
if isinstance(p, str) and p.startswith("/logParsers/"):
paths.append(p)
_os.makedirs(parsers_dir, exist_ok=True)
fetched, failed = 0, []
for p in paths:
name = p.rsplit("/", 1)[-1] or "_unnamed"
try:
r = _post("/api/getFile", {"path": p})
content = r.get("content")
if content is None:
failed.append({"path": p, "error": "no content", "raw": r})
continue
with open(_os.path.join(parsers_dir, name), "w", encoding="utf-8") as fh:
fh.write(content)
fetched += 1
except Exception as e:
failed.append({"path": p, "error": str(e)})
# Surface the raw API response so callers can see exactly what was returned.
# Truncate paths list so the response stays readable (first 200).
debug_info = {
"response_keys": list(res.keys()),
"paths_found": len(paths),
"paths_listed": paths[:200],
}
return {"fetched": fetched, "failed": failed, "skipped": None, "debug": debug_info}
@router.post("/load-parsers-from-sdl")
async def load_parsers_from_sdl(db: Session = Depends(get_db)):
"""
Load SDL parsers from the local /app/parsers directory (mounted from ./parsers/).
Files are placed there by the MCP-based loader or by manual copy.
Falls back to a clear error if the directory is empty.
Sync SDL parsers from the console (if SDL_CONFIG_READ_KEY is set) then index
every file in the local /app/parsers directory into the DB.
"""
import os
parsers_dir = "/app/parsers"
# ── Step 1: fetch from console (best-effort) ────────────────────────────
fetch_result = _fetch_parsers_from_console(parsers_dir)
# ── Step 2: load whatever is on disk into the DB ─────────────────────────
try:
entries = [
e for e in os.scandir(parsers_dir)
@@ -225,12 +318,19 @@ async def load_parsers_from_sdl(db: Session = Depends(get_db)):
except FileNotFoundError:
raise HTTPException(503, "parsers/ directory not found — check Docker volume mount")
if not entries and fetch_result["skipped"]:
raise HTTPException(
422,
f"No parser files found in parsers/ directory and console sync was skipped "
f"({fetch_result['skipped']}). "
"Add SDL_CONFIG_READ_KEY in Settings (needs 'Manage config files' permission) "
"or upload a parser file manually."
)
if not entries:
raise HTTPException(
422,
"No parser files found in parsers/ directory. "
"Use 'Load SDL Parsers via MCP' in Claude Code to populate it, "
"or upload a parser file manually."
"No parser files found in parsers/ directory after console sync. "
"Check SDL_CONFIG_READ_KEY permissions ('Manage config files' required)."
)
loaded = []
@@ -258,7 +358,12 @@ async def load_parsers_from_sdl(db: Session = Depends(get_db)):
errors.append({"parser": entry.name, "error": str(e)})
db.commit()
return {"loaded": len(loaded), "parsers": loaded, "errors": errors}
return {
"loaded": len(loaded),
"parsers": loaded,
"errors": errors,
"console_fetch": fetch_result,
}
@router.post("/upload-parser")
@@ -329,6 +434,9 @@ _S1_NATIVE_SOURCES = {
"SentinelOne Ranger AD",
}
# Cached count of events with no dataSource.name — updated on each sync
_unlabelled_event_count: int = -1 # -1 = not yet queried
@router.post("/sync-sources")
async def sync_sources(days: int = 7, db: Session = Depends(get_db)):
@@ -378,28 +486,34 @@ async def sync_sources(days: int = 7, db: Session = Depends(get_db)):
parser_detected=parsed_by_source.get(name, 0),
))
seen += 1
db.commit()
return {"synced": seen, "sources": [r["dataSource.name"] for r in rows if r.get("dataSource.name") and r["dataSource.name"] not in _S1_NATIVE_SOURCES]}
synced_names = [r["dataSource.name"] for r in rows if r.get("dataSource.name") and r["dataSource.name"] not in _S1_NATIVE_SOURCES]
return {"synced": seen, "sources": synced_names}
def _build_parser_ds_index() -> dict[str, dict]:
def _build_parser_ds_index() -> tuple[dict[str, dict], list[dict]]:
"""
Read all parser files from /app/parsers/ and build an index:
dataSource.name (exact, from parser attributes) {parser_name, format_type}
Read all parser files from /app/parsers/ and build:
- index: dataSource.name {parser_name, format_type} (complete parsers)
- stubs: list of {parser_name} for files with no dataSource.name attribute
Format type is "grok", "dottedJson", or "custom".
Sources with grok/dottedJson parsers are flagged as needing a proper parser.
"""
import os, re
parsers_dir = "/app/parsers"
_DS_NAME_RE = re.compile(r'"dataSource\.name"\s*:\s*"([^"]+)"')
_DS_NAME_RE = re.compile(r'"?dataSource\.name"?\s*:\s*"([^"]+)"')
_FORMAT_TYPE_RE = re.compile(r'"type"\s*:\s*"([^"]+)"')
# Only treat a file as a parser if it has a formats section — rules out dashboards/saved-searches
_HAS_FORMATS_RE = re.compile(r'\bformats\s*:', re.IGNORECASE)
index: dict[str, dict] = {}
stubs: list[dict] = []
try:
entries = [e for e in os.scandir(parsers_dir) if e.is_file() and not e.name.startswith(".")]
except FileNotFoundError:
return index
return index, stubs
for entry in entries:
try:
@@ -408,9 +522,15 @@ def _build_parser_ds_index() -> dict[str, dict]:
except Exception:
continue
# Skip files that have no formats section — they're dashboards/queries, not parsers
if not _HAS_FORMATS_RE.search(content):
continue
# Extract dataSource.name (may appear multiple times — take first)
ds_match = _DS_NAME_RE.search(content)
if not ds_match:
# Has formats but no dataSource.name — genuine stub parser
stubs.append({"parser_name": entry.name})
continue
ds_name = ds_match.group(1).strip()
@@ -425,7 +545,7 @@ def _build_parser_ds_index() -> dict[str, dict]:
index[ds_name] = {"parser_name": entry.name, "format_type": fmt}
return index
return index, stubs
@router.get("/map")
@@ -447,11 +567,20 @@ def get_coverage_map(db: Session = Depends(get_db)):
parser_index.setdefault(pf.parser_name, set()).add(pf.field_name)
# Build dataSource.name → {parser_name, format_type} index from parser files
ds_index = _build_parser_ds_index()
ds_index, stub_parsers = _build_parser_ds_index()
def _normalize(s: str) -> str:
return s.lower().replace(" ", "").replace("-", "").replace("_", "").replace(".", "")
def _find_stub_match(source_name: str) -> dict | None:
"""Return stub parser info if a stub filename fuzzy-matches this source name."""
sn = _normalize(source_name)
for stub in stub_parsers:
fn = _normalize(stub["parser_name"])
if fn in sn or sn in fn:
return stub
return None
def _find_parser_info(source_name: str) -> dict | None:
"""
Match priority:
@@ -514,6 +643,8 @@ def get_coverage_map(db: Session = Depends(get_db)):
parser_info = _find_parser_info(src.source_name)
parser_in_data = (src.parser_detected or 0) > 0
stub_info = _find_stub_match(src.source_name) if not parser_info else None
if parser_info and parser_info["format_type"] == "custom":
status = "covered"
matched_parser = parser_info["parser_name"]
@@ -528,6 +659,12 @@ def get_coverage_map(db: Session = Depends(get_db)):
status = "covered"
matched_parser = parser_info["parser_name"] if parser_info else "detected in data"
format_type = parser_info["format_type"] if parser_info else "unknown"
elif stub_info:
# A parser file exists but has no dataSource.name — it's a stub/incomplete
status = "stub_parser"
matched_parser = stub_info["parser_name"]
format_type = None
stub_info["suggested_ds_name"] = src.source_name
else:
status = "parser_needed"
matched_parser = None
@@ -536,7 +673,7 @@ def get_coverage_map(db: Session = Depends(get_db)):
if status == "covered":
covered_count += 1
else:
needed_count += 1
needed_count += 1 # stub_parser and parser_needed both count as needing work
rules_for_src: list = [r for r in rule_by_source.get(src.source_name, []) if r["type"] == "library"]
@@ -614,6 +751,8 @@ def get_coverage_map(db: Session = Depends(get_db)):
"status": status,
"parser": matched_parser,
"format_type": format_type,
"unlabelled": bool(src.unlabelled),
"stub_suggested_ds_name": stub_info.get("suggested_ds_name") if stub_info and status == "stub_parser" else None,
"parser_fields": len(parser_provides),
"parser_detected": src.parser_detected or 0,
"rules": rules_for_src,
@@ -624,13 +763,20 @@ def get_coverage_map(db: Session = Depends(get_db)):
"synced_at": src.synced_at.isoformat() if src.synced_at else None,
})
# Only surface stub parsers that matched an active source with real events —
# unmatched stubs with zero events are noise and are suppressed.
synced_at = active_sources[0].synced_at.isoformat() if active_sources else None
stub_count = sum(1 for s in sources_out if s["status"] == "stub_parser")
return {
"summary": {
"active_sources": len(active_sources),
"covered": covered_count,
"parser_needed": needed_count,
"stub_parsers": stub_count,
"unlabelled_events": _unlabelled_event_count,
"parsers_loaded": len(parser_index),
"rules_loaded": len(rules),
},
@@ -640,9 +786,20 @@ def get_coverage_map(db: Session = Depends(get_db)):
}
@router.get("/stub-parsers")
def get_stub_parsers():
"""Return all parser files that have a formats: section but no dataSource.name attribute.
Used by Parser Quality Attributes Missing section. Independent of active sources."""
_, stubs = _build_parser_ds_index()
return {"stubs": stubs, "count": len(stubs)}
@router.delete("/reset")
def reset_data(db: Session = Depends(get_db)):
db.query(ParsedRule).delete()
db.query(ParserField).delete()
db.query(ActiveSource).delete()
db.commit()
global _unlabelled_event_count
_unlabelled_event_count = -1
return {"cleared": True}
+146 -28
View File
@@ -38,6 +38,7 @@ class SampleEventsRequest(BaseModel):
source: str
limit: int = 20
hours: int = 1
filter_mode: str = "broad" # reserved for future use
class FieldPopulationRequest(BaseModel):
@@ -107,21 +108,41 @@ def _flatten_event(event: dict) -> dict:
def _extract_format_strings(content: str) -> list[str]:
"""
Extract SDL format string values from augmented-JSON parser content.
Matches: "format": "..." (double-quoted value, supports escaped quotes).
Handles both:
- quoted keys: "format": "..." (valid JSON)
- unquoted keys: format: "..." (SDL augmented-JSON)
Skips commented-out lines (// ...).
"""
pattern = re.compile(r'"format"\s*:\s*"((?:[^"\\]|\\.)*)"')
return pattern.findall(content)
pattern = re.compile(r'(?<!//)\"?format\"?\s*:\s*"((?:[^"\\]|\\.)*)"')
results = []
for line in content.splitlines():
stripped = line.strip()
if stripped.startswith("//"):
continue
results.extend(pattern.findall(line))
return results
def _sdl_format_to_regex(fmt: str) -> tuple[re.Pattern, dict[str, str]]:
"""
Convert an SDL format string to a compiled Python regex.
Returns (compiled_pattern, py_group_to_sdl_field) mapping so callers can
translate group names back to the original SDL field names.
SDL format strings may start with '.*,' to absorb a syslog header. When
used with re.search that prefix is redundant AND harmful (it forces a comma
before the first named field, which won't exist when the log starts with
the field directly). We strip the leading '.*,' so re.search can anchor
to the first real field at any position in the line.
Internal '.*' wildcards (field separators for skipped fields) are kept as
non-greedy '.*?' so they don't consume adjacent named-field values.
Returns (compiled_pattern, py_group_to_sdl_field).
Raises re.error if the resulting pattern cannot be compiled.
"""
# Strip leading/trailing .* wildcards — re.search handles positioning
fmt = re.sub(r'^(\.\*,?)+', '', fmt)
fmt = re.sub(r'(,?\.\*)+$', '', fmt)
# Split on $...$ tokens
token_pattern = re.compile(r'\$([^$]+)\$')
parts = token_pattern.split(fmt)
@@ -131,19 +152,25 @@ def _sdl_format_to_regex(fmt: str) -> tuple[re.Pattern, dict[str, str]]:
py_group_to_sdl: dict[str, str] = {}
seen_groups: dict[str, int] = {}
def _escape_literal(s: str) -> str:
"""Escape literal text but keep internal .* as non-greedy wildcards."""
segments = re.split(r'(\.\*)', s)
return ''.join(r'.*?' if seg == '.*' else re.escape(seg) for seg in segments)
for i, part in enumerate(parts):
if i % 2 == 0:
# Literal text
regex_parts.append(re.escape(part))
# Literal text (possibly containing .* wildcards)
regex_parts.append(_escape_literal(part))
else:
# Token: either "field.name=PATTERN" or just "field.name"
if '=' in part:
field_name, pattern = part.split('=', 1)
else:
field_name = part
pattern = r'[^\s]+'
# Default: match any non-comma chars (SDL CSV fields)
pattern = r'[^,]*'
# Build a valid Python group name
# Build a valid Python named-group identifier
safe = re.sub(r'[.\-]', '_', field_name)
if safe in seen_groups:
seen_groups[safe] += 1
@@ -154,7 +181,7 @@ def _sdl_format_to_regex(fmt: str) -> tuple[re.Pattern, dict[str, str]]:
py_group_to_sdl[safe] = field_name
regex_parts.append(f'(?P<{safe}>{pattern})')
compiled = re.compile(''.join(regex_parts), re.IGNORECASE)
compiled = re.compile(''.join(regex_parts), re.IGNORECASE | re.DOTALL)
return compiled, py_group_to_sdl
@@ -162,6 +189,45 @@ def _sdl_format_to_regex(fmt: str) -> tuple[re.Pattern, dict[str, str]]:
# Endpoints
# ---------------------------------------------------------------------------
@router.post("/sample-unlabelled")
async def sample_unlabelled(req: SampleEventsRequest):
"""Return a sample of events that have no dataSource.name — these need parsers.
Also runs a count query so the caller can update the banner with the real total.
"""
import asyncio
from routers import coverage as _coverage
filter_expr = "!(dataSource.name = *) !(source = 'scalyr')"
from_dt, to_dt = _date_range_hours(req.hours)
sample_result, count_result = await asyncio.gather(
s1_client.run_powerquery(f"{filter_expr} | limit {req.limit}", from_dt, to_dt),
s1_client.run_powerquery(f"{filter_expr} | group events=count()", from_dt, to_dt, max_count=50_000_000),
)
rows = sample_result if isinstance(sample_result, list) else (sample_result.get("rows") or sample_result.get("events") or [])
events = [_flatten_event(row) for row in rows]
non_empty_keys: set = set()
for ev in events:
for k, v in ev.items():
if v is not None and v != "" and v != "null":
non_empty_keys.add(k)
events = [{k: v for k, v in ev.items() if k in non_empty_keys} for ev in events]
count_rows = count_result.get("events", []) if isinstance(count_result, dict) else []
total = count_rows[0].get("events", 0) if count_rows else 0
_coverage._unlabelled_event_count = total
return {
"events": events,
"count": len(events),
"total": total,
"hours": req.hours,
"columns_seen": sorted(non_empty_keys),
}
@router.post("/sample-events")
async def sample_events(req: SampleEventsRequest):
"""Return a sample of raw events from a given data source."""
@@ -196,21 +262,39 @@ async def field_population(req: FieldPopulationRequest):
events = [_flatten_event(row) for row in rows]
if not events:
raise HTTPException(status_code=404, detail=f"No events found for source '{req.source}' in the last {req.hours} hours.")
return {
"source": req.source,
"total_sampled": 0,
"hours": req.hours,
"fields": [],
"fields_seen_in_sample": [],
"message": f"No events found for source '{req.source}' in the last {req.hours} hours.",
}
total = len(events)
_empty = {None, "", "null"}
_empty_scalars = {None, "", "null"}
def _is_empty(val):
"""Return True if the value counts as unpopulated."""
if val is None:
return True
if isinstance(val, list):
return len(val) == 0
if isinstance(val, dict):
return len(val) == 0
return val in _empty_scalars
# Collect all field names seen across the sample (useful for surfacing what IS there)
all_seen_fields = sorted({k for ev in events for k in ev})
all_seen_fields_set = set(all_seen_fields)
field_stats = []
for field in req.fields:
# dataSource.name is always 100% — we filtered by it; Scalyr just doesn't echo it back
if field == "dataSource.name":
populated = total
else:
populated = sum(1 for ev in events if ev.get(field) not in _empty)
# Skip fields that don't appear anywhere in the sample
if field not in all_seen_fields_set:
continue
populated = sum(1 for ev in events if not _is_empty(ev.get(field)))
rate = round((populated / total) * 100, 1)
field_stats.append({
"field": field,
@@ -219,8 +303,8 @@ async def field_population(req: FieldPopulationRequest):
"rate": rate,
})
# Sort ascending by rate (worst coverage first)
field_stats.sort(key=lambda x: x["rate"])
# Sort descending by rate (best coverage first)
field_stats.sort(key=lambda x: x["rate"], reverse=True)
return {
"source": req.source,
@@ -255,7 +339,10 @@ async def test_parser(req: TestParserRequest):
# The regex-based path can't model that — handle it explicitly so users
# can test JSON-shaped logs against JSON-mode parsers.
log_input = req.log_line.strip()
is_json_mode = any("parse=json" in f for f in format_strings) or log_input.startswith("{")
# Only enter JSON mode if the log content actually looks like JSON.
# Don't force it based on the parser type alone — a JSON-capable parser
# should still fall through to regex matching for non-JSON inputs.
is_json_mode = log_input.startswith("{") or log_input.startswith("[")
if is_json_mode:
import json as _json
# Support multi-line input (one JSON object per line, or a JSON array)
@@ -307,18 +394,24 @@ async def test_parser(req: TestParserRequest):
# Use the first payload for the detail table; report totals.
payload = payloads[0]
extracted = _flatten_dict(payload)
# SDL's parse=json puts all keys into unmapped.* namespace first, then
# rewrites map unmapped.X -> ocsf.field. Mirror that so rewrites fire.
unmapped_aliases = {f"unmapped.{k}": v for k, v in extracted.items()}
extracted_with_unmapped = {**extracted, **unmapped_aliases}
# Apply lightweight rewrites if present (input/output/match/replace blocks).
# We only handle simple literal/regex matches with $0 or string replacements;
# this is best-effort, intended for quick visual verification.
rewrites_applied = []
# Handle both quoted keys ("input":) and unquoted keys (input:)
rewrite_re = re.compile(
r'\{\s*input:\s*"([^"]+)"\s*,\s*output:\s*"([^"]+)"\s*,\s*match:\s*"((?:[^"\\]|\\.)*)"\s*,\s*replace:\s*"((?:[^"\\]|\\.)*)"\s*\}',
r'\{\s*"?input"?\s*:\s*"([^"]+)"\s*,\s*"?output"?\s*:\s*"([^"]+)"\s*,\s*"?match"?\s*:\s*"((?:[^"\\]|\\.)*)"\s*,\s*"?replace"?\s*:\s*"((?:[^"\\]|\\.)*)"\s*\}',
re.DOTALL,
)
derived: dict[str, str] = {}
for m in rewrite_re.finditer(content):
in_field, out_field, match_pat, replace_val = m.group(1), m.group(2), m.group(3), m.group(4)
src_val = extracted.get(in_field)
src_val = extracted_with_unmapped.get(in_field)
if src_val is None:
continue
try:
@@ -359,32 +452,57 @@ async def test_parser(req: TestParserRequest):
"showing_payload": 1,
}
# ── Regex format-string path (original) ─────────────────────────────────
# ── Regex format-string path ─────────────────────────────────────────────
def _try_prefix_match(compiled: re.Pattern, py_to_sdl: dict, log_line: str):
"""
Try the full pattern; if it doesn't match, progressively shorten from
the right (group by group) until we get a match. This handles logs
that don't include all the trailing optional fields the parser defines.
Returns (match, truncated) or (None, False).
"""
m = compiled.search(log_line)
if m:
return m, False
# Shorten pattern by removing trailing named groups one at a time
p = compiled.pattern
# Find all (?P<name>...) group end positions (right to left)
group_ends = [m2.end() for m2 in re.finditer(r'\(\?P<[^>]+>[^)]*\)', p)]
for end in reversed(group_ends[1:]): # keep at least 1 group
try:
shorter = re.compile(p[:end], re.IGNORECASE | re.DOTALL)
m2 = shorter.search(log_line)
if m2:
return m2, True
except re.error:
continue
return None, False
for fmt in format_strings:
try:
compiled, py_to_sdl = _sdl_format_to_regex(fmt)
except re.error:
# Skip unparseable format strings
continue
match = compiled.search(req.log_line)
match, truncated = _try_prefix_match(compiled, py_to_sdl, req.log_line)
if match:
fields = [
{"field": py_to_sdl.get(group, group), "value": value}
for group, value in match.groupdict().items()
if value is not None
if value is not None and value != ""
]
return {
"parser_name": req.parser_name,
"matched": True,
"mode": "regex",
"format_matched": fmt,
"format_matched": fmt[:120] + ("" if len(fmt) > 120 else ""),
"fields": fields,
"note": "Partial match — log has fewer fields than the full parser format" if truncated else None,
}
return {
"parser_name": req.parser_name,
"matched": False,
"message": "No format pattern matched",
"message": "No format pattern matched. Check that the log includes the log-type keyword (e.g. TRAFFIC, THREAT) and enough comma-separated fields.",
"fields": [],
}
+1
View File
@@ -14,6 +14,7 @@ FIELDS = [
{"key": "S1_API_TOKEN", "label": "Console API Token", "secret": True, "placeholder": "eyJ..."},
{"key": "SDL_XDR_URL", "label": "SDL XDR URL", "secret": False, "placeholder": "https://xdr.us1.sentinelone.net"},
{"key": "SDL_LOG_READ_KEY", "label": "SDL Log Read Key", "secret": True, "placeholder": "1DnK0Y4e..."},
{"key": "SDL_CONFIG_READ_KEY", "label": "SDL Config Read Key", "secret": True, "placeholder": "Needs 'Manage config files' permission"},
{"key": "ANTHROPIC_API_KEY", "label": "Anthropic API Key", "secret": True, "placeholder": "sk-ant-..."},
]
+121 -17
View File
@@ -11,6 +11,11 @@ TOKEN = os.environ.get("S1_API_TOKEN", "")
SDL_XDR_URL = os.environ.get("SDL_XDR_URL", "https://xdr.us1.sentinelone.net").rstrip("/")
SDL_LOG_READ_KEY = os.environ.get("SDL_LOG_READ_KEY", "")
# SDL Configuration Read Key — used to list/fetch parser files under /logParsers/
# (separate from SDL_LOG_READ_KEY which is for querying events only).
# Find it in the S1 console: Settings → Integrations → Data Lake API Keys → Configuration Read.
SDL_CONFIG_READ_KEY = os.environ.get("SDL_CONFIG_READ_KEY", "")
# Management Console API uses ApiToken auth
HEADERS = {
"Authorization": f"ApiToken {TOKEN}",
@@ -92,7 +97,7 @@ async def get_library_rules(page_size: int = 100) -> list:
return results
async def run_powerquery(query: str, from_date: str, to_date: str) -> dict:
async def run_powerquery(query: str, from_date: str, to_date: str, max_count: int = 1000) -> dict:
"""
Run a PowerQuery against the Singularity Data Lake via the Scalyr XDR API.
Uses SDL_XDR_URL + SDL_LOG_READ_KEY (Scalyr readlog token).
@@ -109,7 +114,7 @@ async def run_powerquery(query: str, from_date: str, to_date: str) -> dict:
"query": query,
"startTime": start_ms,
"endTime": end_ms,
"maxCount": 1000,
"maxCount": max_count,
}
async with httpx.AsyncClient(timeout=120) as client:
@@ -154,8 +159,47 @@ async def run_powerquery(query: str, from_date: str, to_date: str) -> dict:
return {"events": matches}
def _sdl_config_headers() -> dict:
"""Auth headers for the SDL Configuration File API (uses POST /api/listFiles,
POST /api/getFile, etc.). Falls back to SDL_LOG_READ_KEY if no dedicated
Configuration Read key is set that won't work for all endpoints, but lets
callers fail with a meaningful 401 instead of crashing."""
key = SDL_CONFIG_READ_KEY or SDL_LOG_READ_KEY
return {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
}
async def list_sdl_parsers() -> list[str]:
"""List all parser filenames under /logParsers/ in SDL."""
"""List parser paths under /logParsers/ via the SDL Configuration File API.
Requires SDL_CONFIG_READ_KEY (or higher) in .env. The endpoint is
POST <SDL_XDR_URL>/api/listFiles with {"pathPrefix": "/logParsers/"}.
Returns names without the /logParsers/ prefix, suitable for use as
filenames in the local parsers/ directory.
"""
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.post(
f"{SDL_XDR_URL}/api/listFiles",
headers=_sdl_config_headers(),
json={"pathPrefix": "/logParsers/"},
)
resp.raise_for_status()
data = resp.json()
paths = data.get("paths") or data.get("files") or []
# Normalize: strip leading /logParsers/ and ignore anything that isn't there
names: list[str] = []
for p in paths:
if isinstance(p, dict):
p = p.get("path") or p.get("name") or ""
if isinstance(p, str) and p.startswith("/logParsers/"):
names.append(p[len("/logParsers/"):])
return names
async def list_sdl_parsers_legacy() -> list[str]:
"""[Deprecated] Legacy management-console path — kept for reference but unused."""
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.get(
f"{BASE_URL}/api/v1/files/logParsers",
@@ -170,46 +214,106 @@ async def list_sdl_parsers() -> list[str]:
async def get_sdl_parser(filename: str) -> dict:
"""Fetch a single SDL parser file by name."""
"""Fetch a single SDL parser file by name via POST /api/getFile.
Returns the raw SDL response dict, e.g.
{"status": "success", "path": "/logParsers/Foo", "content": "...", "version": 3, ...}
"""
path = filename if filename.startswith("/logParsers/") else f"/logParsers/{filename}"
async with httpx.AsyncClient(timeout=30) as client:
resp = await client.get(
f"{BASE_URL}/api/v1/files/logParsers/{filename}",
headers=HEADERS,
resp = await client.post(
f"{SDL_XDR_URL}/api/getFile",
headers=_sdl_config_headers(),
json={"path": path},
)
resp.raise_for_status()
return resp.json()
async def get_account_id() -> str | None:
"""Return the first account ID visible to the current token."""
"""Return the first account ID visible to the current token.
Tries /accounts first (works for account-scoped or higher tokens). If that
returns 403 (site-scoped token), falls back to /sites and reads accountId
from the first site.
"""
async with httpx.AsyncClient(timeout=15) as client:
# Path 1: account-scoped token
resp = await client.get(
f"{BASE_URL}/web/api/v2.1/accounts",
headers=HEADERS,
params={"limit": 1},
)
resp.raise_for_status()
accounts = resp.json().get("data", [])
return str(accounts[0]["id"]) if accounts else None
if resp.status_code == 200:
accounts = resp.json().get("data", [])
if accounts:
return str(accounts[0]["id"])
# Path 2: site-scoped token — accountId is embedded in sites payload
if resp.status_code in (401, 403):
sresp = await client.get(
f"{BASE_URL}/web/api/v2.1/sites",
headers=HEADERS,
params={"limit": 1},
)
if sresp.status_code == 200:
data = sresp.json().get("data", {})
sites = data.get("sites") if isinstance(data, dict) else data
if sites:
return str(sites[0].get("accountId") or "") or None
return None
async def get_scope_for_platform_rules() -> tuple[str, str] | None:
"""Pick the best scope for /detection-library/platform-rules.
Returns (scopeLevel, scopeId). Tries account first, then site site-scoped
tokens cannot list accounts but CAN query platform-rules with site scope.
"""
async with httpx.AsyncClient(timeout=15) as client:
# Prefer account scope (broadest)
a = await client.get(
f"{BASE_URL}/web/api/v2.1/accounts",
headers=HEADERS,
params={"limit": 1},
)
if a.status_code == 200:
accounts = a.json().get("data", [])
if accounts:
return ("account", str(accounts[0]["id"]))
# Fall back to site scope (site-scoped tokens land here)
s = await client.get(
f"{BASE_URL}/web/api/v2.1/sites",
headers=HEADERS,
params={"limit": 1},
)
if s.status_code == 200:
data = s.json().get("data", {})
sites = data.get("sites") if isinstance(data, dict) else data
if sites:
sid = sites[0].get("id")
if sid:
return ("site", str(sid))
return None
async def get_platform_rules(page_size: int = 1000) -> list:
"""
Fetch all Detection Library platform rules from /detection-library/platform-rules.
Requires scopeLevel + scopeId uses account scope with the first visible account.
Returns list of rules, each with a 'sources' list (authoritative data source names).
Requires scopeLevel + scopeId. Tries account scope first, then site scope so
site-scoped tokens also work.
"""
account_id = await get_account_id()
if not account_id:
scope = await get_scope_for_platform_rules()
if not scope:
return []
scope_level, scope_id = scope
all_rules: list = []
cursor: str = ""
async with httpx.AsyncClient(timeout=60) as client:
while True:
params: dict = {
"scopeLevel": "account",
"scopeId": account_id,
"scopeLevel": scope_level,
"scopeId": scope_id,
"limit": page_size,
"cursor": cursor,
}
+1
View File
@@ -15,6 +15,7 @@ services:
- S1_BASE_URL=${S1_BASE_URL}
- SDL_XDR_URL=${SDL_XDR_URL}
- SDL_LOG_READ_KEY=${SDL_LOG_READ_KEY}
- SDL_CONFIG_READ_KEY=${SDL_CONFIG_READ_KEY}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
- DATABASE_URL=postgresql://siem:siem@db:5432/siem
- DETECTIONS_FILE=/app/data/detections.json
+488 -237
View File
File diff suppressed because it is too large Load Diff
+949 -21
View File
@@ -1,29 +1,957 @@
{
"attributes": {
"dataSource.vendor": "AWS",
"dataSource.name": "AWS Web Application Firewall",
"dataSource.category": "web_security"
"dataSource.name": "AWS CloudTrail",
"dataSource.category": "security",
"metadata.product.vendor_name": "AWS",
"metadata.product.name": "AWS CloudTrail",
"metadata.version": "1.0.0"
},
"formats": [
{
"id": "aws_waf_json",
"format": ".*${parse=json}$",
"rewrites": [
{ "input": "timestamp", "output": "time", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.clientIp", "output": "src_endpoint.ip", "match": ".*", "replace": "$0" },
{ "input": "action", "output": "disposition", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.uri", "output": "http_request.url.text", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.country", "output": "src_endpoint.location.country", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.httpMethod", "output": "http_request.http_method", "match": ".*", "replace": "$0" },
{ "input": "webaclId", "output": "firewall_rule.uid", "match": ".*", "replace": "$0" },
{ "input": "ruleGroupId", "output": "firewall_rule.name", "match": ".*", "replace": "$0" },
{ "input": "terminatingRuleType", "output": "firewall_rule.type", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.httpVersion", "output": "http_request.version", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.args", "output": "http_request.url.query_string", "match": ".*", "replace": "$0" },
{ "input": "requestId", "output": "http_request.uid", "match": ".*", "replace": "$0" },
{ "input": "httpRequest.headers", "output": "http_request.http_headers", "match": ".*", "replace": "$0" }
],
"halt": true
"format": "${parse=gron}$",
"skipNumericConversion": true
}
]
],
"mappings": {
"version": 1,
"mappings": [
{
"predicate": "eventCategory matches '.*'",
"transformations": [
{
"constant": {
"field": "$s1_tmp.predicate_0",
"value": true,
"predicate": "userIdentity.arn matches '.*'"
}
}, {
"rename_tree": {
"from": "",
"to": "unmapped"
}
}, {
"copy": {
"to": "message",
"from": "unmapped.message"
}
}, {
"drop": {
"field": "unmapped.message"
}
}, {
"constant": {
"field": "class_uid",
"value": 4002
}
}, {
"constant": {
"field": "metadata.product.name",
"value": "AWS CloudTrail"
}
}, {
"constant": {
"field": "metadata.product.vendor_name",
"value": "AWS"
}
}, {
"constant": {
"field": "metadata.version",
"value": "1.0.0-rc3"
}
}, {
"constant": {
"field": "category_name",
"value": "Network Activity"
}
}, {
"constant": {
"field": "category_uid",
"value": 4
}
}, {
"constant": {
"field": "class_uid",
"value": 4002
}
}, {
"constant": {
"field": "class_name",
"value": "HTTP Activity"
}
}, {
"constant": {
"field": "metadata.product.name",
"value": "CloudTrail"
}
}, {
"constant": {
"field": "metadata.product.vendor_name",
"value": "AWS"
}
}, {
"constant": {
"field": "metadata.version",
"value": "1.0.0-rc3"
}
}, {
"constant": {
"field": "type_name",
"value": "HTTP Activity: Other"
}
}, {
"constant": {
"field": "type_uid",
"value": 400299
}
}, {
"constant": {
"field": "activity_id",
"value": 99
}
}, {
"constant": {
"field": "severity_id",
"value": 99
}
}, {
"constant": {
"field": "status_id",
"value": 99
}
}, {
"constant": {
"field": "status",
"value": "Other"
}
}, {
"constant": {
"field": "dataSource.vendor",
"value": "AWS"
}
}, {
"constant": {
"field": "dataSource.name",
"value": "CloudTrail"
}
}, {
"constant": {
"field": "dataSource.category",
"value": "security"
}
}, {
"constant": {
"field": "observables[0].type_id",
"value": 2
}
}, {
"constant": {
"field": "observables[0].type",
"value": "IP Address"
}
}, {
"constant": {
"field": "observables[0].name",
"value": "src_endpoint.ip"
}
}, {
"constant": {
"field": "observables[1].type_id",
"value": 99,
"predicate": "unmapped.$s1_tmp.predicate_0 == true"
}
}, {
"constant": {
"field": "observables[1].type",
"value": "Other",
"predicate": "unmapped.$s1_tmp.predicate_0 == true"
}
}, {
"constant": {
"field": "observables[1].name",
"value": "unmapped.userIdentity.arn",
"predicate": "unmapped.$s1_tmp.predicate_0 == true"
}
}, {
"copy": {
"to": "cloud.region",
"from": "unmapped.awsRegion"
}
}, {
"copy": {
"to": "metadata.product.feature.name",
"from": "unmapped.eventCategory"
}
}, {
"copy": {
"to": "metadata.uid",
"from": "unmapped.eventID"
}
}, {
"copy": {
"to": "unmapped.eventName",
"from": "unmapped.eventName"
}
}, {
"copy": {
"to": "api.service.name",
"from": "unmapped.eventSource"
}
}, {
"copy": {
"to": "metadata.original_time",
"from": "unmapped.eventTime"
}
}, {
"copy": {
"to": "unmapped.eventType",
"from": "unmapped.eventType"
}
}, {
"copy": {
"to": "metadata.product.version",
"from": "unmapped.eventVersion"
}
}, {
"copy": {
"to": "unmapped.managementEvent",
"from": "unmapped.managementEvent"
}
}, {
"copy": {
"to": "unmapped.readOnly",
"from": "unmapped.readOnly"
}
}, {
"copy": {
"to": "cloud.account.uid",
"from": "unmapped.recipientAccountId"
}
}, {
"copy": {
"to": "api.request.uid",
"from": "unmapped.requestID"
}
}, {
"copy": {
"to": "duration",
"from": "unmapped.requestParameters.durationSeconds"
}
}, {
"copy": {
"to": "unmapped.requestParameters.roleArn",
"from": "unmapped.requestParameters.roleArn"
}
}, {
"copy": {
"to": "unmapped.requestParameters.roleSessionName",
"from": "unmapped.requestParameters.roleSessionName"
}
}, {
"copy": {
"to": "api.request.uid",
"from": "unmapped.requestParameters.externalId"
}
}, {
"copy": {
"to": "resource.account.uid[*]",
"from": "unmapped.resources[*].accountId"
}
}, {
"copy": {
"to": "resource.type[*]",
"from": "unmapped.resources[*].type"
}
}, {
"copy": {
"to": "resource.uid[*]",
"from": "unmapped.resources[*].ARN"
}
}, {
"copy": {
"to": "unmapped.responseElements.assumedRoleUser.assumedRoleId",
"from": "unmapped.responseElements.assumedRoleUser.assumedRoleId"
}
}, {
"copy": {
"to": "unmapped.responseElements.assumedRoleUser.arn",
"from": "unmapped.responseElements.assumedRoleUser.arn"
}
}, {
"copy": {
"to": "actor.session.credential_uid",
"from": "unmapped.responseElements.credentials.accessKeyId"
}
}, {
"copy": {
"to": "unmapped.responseElements.credentials.sessionToken",
"from": "unmapped.responseElements.credentials.sessionToken"
}
}, {
"copy": {
"to": "actor.session.expiration_time",
"from": "unmapped.responseElements.credentials.expiration"
}
}, {
"copy": {
"to": "unmapped.responseElements.sourceIdentity",
"from": "unmapped.responseElements.sourceIdentity"
}
}, {
"copy": {
"to": "unmapped.sharedEventID",
"from": "unmapped.sharedEventID"
}
}, {
"copy": {
"to": "src_endpoint.ip",
"from": "unmapped.sourceIPAddress"
}
}, {
"copy": {
"to": "tls.version",
"from": "unmapped.tlsDetails.tlsVersion"
}
}, {
"copy": {
"to": "tls.cipher",
"from": "unmapped.tlsDetails.cipherSuite"
}
}, {
"copy": {
"to": "unmapped.tlsDetails.clientProvidedHostHeader",
"from": "unmapped.tlsDetails.clientProvidedHostHeader"
}
}, {
"copy": {
"to": "http_request.user_agent",
"from": "unmapped.userAgent"
}
}, {
"copy": {
"to": "actor.user.account.uid",
"from": "unmapped.userIdentity.accountId"
}
}, {
"copy": {
"to": "actor.user.uid",
"from": "unmapped.userIdentity.principalId"
}
}, {
"copy": {
"to": "actor.user.type",
"from": "unmapped.userIdentity.type"
}
}, {
"copy": {
"to": "unmapped.additionalEventData.SignatureVersion",
"from": "unmapped.additionalEventData.SignatureVersion"
}
}, {
"copy": {
"to": "unmapped.additionalEventData.CipherSuite",
"from": "unmapped.additionalEventData.CipherSuite"
}
}, {
"copy": {
"to": "unmapped.additionalEventData.bytesTransferredIn",
"from": "unmapped.additionalEventData.bytesTransferredIn"
}
}, {
"copy": {
"to": "unmapped.additionalEventData.AuthenticationMethod",
"from": "unmapped.additionalEventData.AuthenticationMethod"
}
}, {
"copy": {
"to": "resources.uid",
"from": "unmapped.additionalEventData.x-amz-id-2"
}
}, {
"copy": {
"to": "unmapped.additionalEventData.bytesTransferredOut",
"from": "unmapped.additionalEventData.bytesTransferredOut"
}
}, {
"copy": {
"to": "resources.name",
"from": "unmapped.requestParameters.bucketName"
}
}, {
"copy": {
"to": "src_endpoint.hostname",
"from": "unmapped.requestParameters.Host"
}
}, {
"copy": {
"to": "unmapped.requestParameters.acl",
"from": "unmapped.requestParameters.acl"
}
}, {
"copy": {
"to": "actor.invoked_by",
"from": "unmapped.userIdentity.invokedBy"
}
}, {
"copy": {
"to": "unmapped.requestParameters.keySpec",
"from": "unmapped.requestParameters.keySpec"
}
}, {
"copy": {
"to": "unmapped.requestParameters.keyId",
"from": "unmapped.requestParameters.keyId"
}
}, {
"copy": {
"to": "unmapped.requestParameters.encryptionContext.aws:cloudtrail:arn",
"from": "unmapped.requestParameters.encryptionContext.aws:cloudtrail:arn"
}
}, {
"copy": {
"to": "unmapped.requestParameters.encryptionContext.aws:s3:arn",
"from": "unmapped.requestParameters.encryptionContext.aws:s3:arn"
}
}, {
"copy": {
"to": "unmapped.requestParameters.agentVersion",
"from": "unmapped.requestParameters.agentVersion"
}
}, {
"copy": {
"to": "unmapped.requestParameters.agentStatus",
"from": "unmapped.requestParameters.agentStatus"
}
}, {
"copy": {
"to": "unmapped.requestParameters.platformType",
"from": "unmapped.requestParameters.platformType"
}
}, {
"copy": {
"to": "unmapped.requestParameters.platformName",
"from": "unmapped.requestParameters.platformName"
}
}, {
"copy": {
"to": "unmapped.requestParameters.platformVersion",
"from": "unmapped.requestParameters.platformVersion"
}
}, {
"copy": {
"to": "unmapped.requestParameters.iPAddress",
"from": "unmapped.requestParameters.iPAddress"
}
}, {
"copy": {
"to": "unmapped.requestParameters.computerName",
"from": "unmapped.requestParameters.computerName"
}
}, {
"copy": {
"to": "unmapped.requestParameters.agentName",
"from": "unmapped.requestParameters.agentName"
}
}, {
"copy": {
"to": "src_endpoint.instance_uid",
"from": "unmapped.requestParameters.instanceId"
}
}, {
"copy": {
"to": "unmapped.requestParameters.maxResults",
"from": "unmapped.requestParameters.maxResults"
}
}, {
"copy": {
"to": "cloud.zone",
"from": "unmapped.requestParameters.availabilityZone"
}
}, {
"copy": {
"to": "unmapped.requestParameters.availabilityZoneId",
"from": "unmapped.requestParameters.availabilityZoneId"
}
}, {
"copy": {
"to": "actor.user.credential_uid",
"from": "unmapped.userIdentity.accessKeyId"
}
}, {
"copy": {
"to": "unmapped.userIdentity.sessionContext.webIdFederationData",
"from": "unmapped.userIdentity.sessionContext.webIdFederationData"
}
}, {
"copy": {
"to": "actor.user.name",
"from": "unmapped.userIdentity.sessionContext.sessionIssuer.type"
}
}, {
"copy": {
"to": "actor.session.uid",
"from": "unmapped.userIdentity.sessionContext.sessionIssuer.principalId"
}
}, {
"copy": {
"to": "actor.session.issuer",
"from": "unmapped.userIdentity.sessionContext.sessionIssuer.arn"
}
}, {
"copy": {
"to": "actor.user.account.uid",
"from": "unmapped.userIdentity.sessionContext.sessionIssuer.accountId"
}
}, {
"copy": {
"to": "actor.session.issuer",
"from": "unmapped.userIdentity.sessionContext.sessionIssuer.userName"
}
}, {
"copy": {
"to": "unmapped.userIdentity.sessionContext.ec2RoleDelivery",
"from": "unmapped.userIdentity.sessionContext.ec2RoleDelivery"
}
}, {
"copy": {
"to": "actor.session.created_time",
"from": "unmapped.userIdentity.sessionContext.attributes.creationDate"
}
}, {
"cast": {
"field": "actor.session.created_time",
"type": "iso8601TimestampToEpochSec"
}
}, {
"copy": {
"to": "unmapped.userIdentity.sessionContext.attributes.mfaAuthenticated",
"from": "unmapped.userIdentity.sessionContext.attributes.mfaAuthenticated"
}
}, {
"copy": {
"to": "unmapped.userIdentity.arn",
"from": "unmapped.userIdentity.arn"
}
}, {
"copy": {
"to": "actor.user.name",
"from": "unmapped.userIdentity.userName"
}
}, {
"copy": {
"to": "api.response.error",
"from": "unmapped.errorCode"
}
}, {
"copy": {
"to": "api.response.error_message",
"from": "unmapped.errorMessage"
}
}, {
"copy": {
"to": "unmapped.edgeDeviceDetails",
"from": "unmapped.edgeDeviceDetails"
}
}, {
"copy": {
"to": "unmapped.sessionCredentialFromConsole",
"from": "unmapped.sessionCredentialFromConsole"
}
}, {
"copy": {
"to": "src_endpoint.uid",
"from": "unmapped.vpcEndpointId"
}
}, {
"copy": {
"to": "unmapped.serviceEventDetails",
"from": "unmapped.serviceEventDetails"
}
}, {
"copy": {
"to": "api.version",
"from": "unmapped.apiVersion"
}
}, {
"copy": {
"to": "unmapped.requestParameters.policy",
"from": "unmapped.requestParameters.policy"
}
}, {
"copy": {
"to": "unmapped.requestParameters.encryption",
"from": "unmapped.requestParameters.encryption"
}
}, {
"copy": {
"to": "unmapped.requestParameters.publicAccessBlock",
"from": "unmapped.requestParameters.publicAccessBlock"
}
}, {
"copy": {
"to": "unmapped.requestParameters.topicArn",
"from": "unmapped.requestParameters.topicArn"
}
}, {
"copy": {
"to": "unmapped.requestParameters.detectorId",
"from": "unmapped.requestParameters.detectorId"
}
}, {
"copy": {
"to": "unmapped.requestParameters.website",
"from": "unmapped.requestParameters.website"
}
}, {
"copy": {
"to": "unmapped.requestParameters.nextToken",
"from": "unmapped.requestParameters.nextToken"
}
}, {
"copy": {
"to": "unmapped.requestParameters.certificateArn",
"from": "unmapped.requestParameters.certificateArn"
}
}, {
"copy": {
"to": "unmapped.requestParameters.ownershipControls",
"from": "unmapped.requestParameters.ownershipControls"
}
}, {
"copy": {
"to": "unmapped.requestParameters.maxRecords",
"from": "unmapped.requestParameters.maxRecords"
}
}, {
"copy": {
"to": "unmapped.requestParameters.DescribeInstanceTypesRequest.NextToken",
"from": "unmapped.requestParameters.DescribeInstanceTypesRequest.NextToken"
}
}, {
"copy": {
"to": "unmapped.requestParameters.DescribeInstanceTypesRequest.MaxResults",
"from": "unmapped.requestParameters.DescribeInstanceTypesRequest.MaxResults"
}
}, {
"copy": {
"to": "unmapped.requestParameters.resourceIds",
"from": "unmapped.requestParameters.resourceIds"
}
}, {
"copy": {
"to": "unmapped.requestParameters.dBSnapshotIdentifier",
"from": "unmapped.requestParameters.dBSnapshotIdentifier"
}
}, {
"copy": {
"to": "unmapped.requestParameters.includeShared",
"from": "unmapped.requestParameters.includeShared"
}
}, {
"copy": {
"to": "unmapped.requestParameters.includePublic",
"from": "unmapped.requestParameters.includePublic"
}
}, {
"copy": {
"to": "unmapped.requestParameters.resourceIdList",
"from": "unmapped.requestParameters.resourceIdList"
}
}, {
"copy": {
"to": "unmapped.requestParameters.logGroupName",
"from": "unmapped.requestParameters.logGroupName"
}
}, {
"copy": {
"to": "unmapped.requestParameters.replication",
"from": "unmapped.requestParameters.replication"
}
}, {
"copy": {
"to": "unmapped.requestParameters.versioning",
"from": "unmapped.requestParameters.versioning"
}
}, {
"copy": {
"to": "unmapped.requestParameters.tagging",
"from": "unmapped.requestParameters.tagging"
}
}, {
"copy": {
"to": "unmapped.requestParameters.logging",
"from": "unmapped.requestParameters.logging"
}
}, {
"copy": {
"to": "unmapped.requestParameters.workGroup",
"from": "unmapped.requestParameters.workGroup"
}
}, {
"copy": {
"to": "unmapped.requestParameters.clusterStates",
"from": "unmapped.requestParameters.clusterStates"
}
}, {
"copy": {
"to": "unmapped.requestParameters.DescribeVpcEndpointsRequest",
"from": "unmapped.requestParameters.DescribeVpcEndpointsRequest"
}
}, {
"copy": {
"to": "unmapped.requestParameters.GetEbsDefaultKmsKeyIdRequest",
"from": "unmapped.requestParameters.GetEbsDefaultKmsKeyIdRequest"
}
}, {
"copy": {
"to": "unmapped.requestParameters.DescribeVpcEndpointServiceConfigurationsRequest",
"from": "unmapped.requestParameters.DescribeVpcEndpointServiceConfigurationsRequest"
}
}, {
"copy": {
"to": "unmapped.requestParameters.DescribeTransitGatewaysRequest",
"from": "unmapped.requestParameters.DescribeTransitGatewaysRequest"
}
}, {
"copy": {
"to": "api.request.uid",
"from": "unmapped.requestParameters.requestContext.awsAccountId"
}
}, {
"copy": {
"to": "unmapped.insightDetails.state",
"from": "unmapped.insightDetails.state"
}
}, {
"copy": {
"to": "api.service.name",
"from": "unmapped.insightDetails.eventSource"
}
}, {
"copy": {
"to": "unmapped.insightDetails.eventName",
"from": "unmapped.insightDetails.eventName"
}
}, {
"copy": {
"to": "unmapped.insightDetails.insightType",
"from": "unmapped.insightDetails.insightType"
}
}, {
"copy": {
"to": "unmapped.insightDetails.insightContext.statistics.baseline.average",
"from": "unmapped.insightDetails.insightContext.statistics.baseline.average"
}
}, {
"copy": {
"to": "unmapped.insightDetails.insightContext.statistics.insight.average",
"from": "unmapped.insightDetails.insightContext.statistics.insight.average"
}
}, {
"copy": {
"to": "duration",
"from": "unmapped.insightDetails.insightContext.statistics.insightDuration"
}
}, {
"copy": {
"to": "event.type",
"from": "unmapped.eventName"
}
}, {
"copy": {
"to": "activity_name",
"from": "unmapped.eventName"
}
}, {
"copy": {
"to": "observables[0].value",
"from": "unmapped.sourceIPAddress"
}
}, {
"copy": {
"to": "observables[1].value",
"from": "unmapped.userIdentity.arn",
"predicate": "unmapped.$s1_tmp.predicate_0 == true"
}
}, {
"drop": {
"field": "unmapped.awsRegion"
}
}, {
"drop": {
"field": "unmapped.eventCategory"
}
}, {
"drop": {
"field": "unmapped.eventID"
}
}, {
"drop": {
"field": "unmapped.eventSource"
}
}, {
"drop": {
"field": "unmapped.eventTime"
}
}, {
"drop": {
"field": "unmapped.eventVersion"
}
}, {
"drop": {
"field": "unmapped.recipientAccountId"
}
}, {
"drop": {
"field": "unmapped.requestID"
}
}, {
"drop": {
"field": "unmapped.requestParameters.durationSeconds"
}
}, {
"drop": {
"field": "unmapped.requestParameters.externalId"
}
}, {
"drop": {
"field": "unmapped.resources[*].accountId"
}
}, {
"drop": {
"field": "unmapped.resources[*].type"
}
}, {
"drop": {
"field": "unmapped.resources[*].ARN"
}
}, {
"drop": {
"field": "unmapped.responseElements.credentials.accessKeyId"
}
}, {
"drop": {
"field": "unmapped.responseElements.credentials.expiration"
}
}, {
"drop": {
"field": "unmapped.sourceIPAddress"
}
}, {
"drop": {
"field": "unmapped.tlsDetails.tlsVersion"
}
}, {
"drop": {
"field": "unmapped.tlsDetails.cipherSuite"
}
}, {
"drop": {
"field": "unmapped.userAgent"
}
}, {
"drop": {
"field": "unmapped.userIdentity.accountId"
}
}, {
"drop": {
"field": "unmapped.userIdentity.principalId"
}
}, {
"drop": {
"field": "unmapped.userIdentity.type"
}
}, {
"drop": {
"field": "unmapped.additionalEventData.x-amz-id-2"
}
}, {
"drop": {
"field": "unmapped.requestParameters.bucketName"
}
}, {
"drop": {
"field": "unmapped.requestParameters.Host"
}
}, {
"drop": {
"field": "unmapped.userIdentity.invokedBy"
}
}, {
"drop": {
"field": "unmapped.requestParameters.instanceId"
}
}, {
"drop": {
"field": "unmapped.requestParameters.availabilityZone"
}
}, {
"drop": {
"field": "unmapped.userIdentity.accessKeyId"
}
}, {
"drop": {
"field": "unmapped.userIdentity.sessionContext.sessionIssuer.type"
}
}, {
"drop": {
"field": "unmapped.userIdentity.sessionContext.sessionIssuer.principalId"
}
}, {
"drop": {
"field": "unmapped.userIdentity.sessionContext.sessionIssuer.arn"
}
}, {
"drop": {
"field": "unmapped.userIdentity.sessionContext.sessionIssuer.accountId"
}
}, {
"drop": {
"field": "unmapped.userIdentity.sessionContext.sessionIssuer.userName"
}
}, {
"drop": {
"field": "unmapped.userIdentity.sessionContext.attributes.creationDate"
}
}, {
"drop": {
"field": "unmapped.userIdentity.userName"
}
}, {
"drop": {
"field": "unmapped.errorCode"
}
}, {
"drop": {
"field": "unmapped.errorMessage"
}
}, {
"drop": {
"field": "unmapped.vpcEndpointId"
}
}, {
"drop": {
"field": "unmapped.apiVersion"
}
}, {
"drop": {
"field": "unmapped.requestParameters.requestContext.awsAccountId"
}
}, {
"drop": {
"field": "unmapped.insightDetails.eventSource"
}
}, {
"drop": {
"field": "unmapped.insightDetails.insightContext.statistics.insightDuration"
}
}, {
"drop": {
"field": "unmapped.$s1_tmp.predicate_0"
}
}
]
}
]
}
}
+158 -19
View File
@@ -2,14 +2,27 @@
"attributes": {
"dataSource.vendor": "Cisco",
"dataSource.name": "Cisco Duo Security",
"dataSource.category": "security"
"dataSource.category": "security",
"metadata.product.vendor_name": "Cisco",
"metadata.product.name": "Cisco Duo Security",
"metadata.version": "1.0.0"
},
"formats": [
{
"format": "$unmapped.{parse=json}$",
"rewrites": [
{ "input": "unmapped.timestamp", "output": "timestamp", "match": ".*", "replace": "$0" },
{ "input": "unmapped.time", "output": "time", "match": ".*", "replace": "$0" }
{
"input": "unmapped.timestamp",
"output": "timestamp",
"match": ".*",
"replace": "$0"
},
{
"input": "unmapped.time",
"output": "time",
"match": ".*",
"replace": "$0"
}
]
}
],
@@ -19,24 +32,150 @@
{
"predicate": "unmapped.class_uid = '3002'",
"transformations": [
{ "copy": { "from": "unmapped.activity_id", "to": "activity_id" } },
{ "copy": { "from": "unmapped.activity_name", "to": "activity_name" } },
{ "copy": { "from": "unmapped.type_uid", "to": "type_uid" } },
{ "copy": { "from": "unmapped.severity_id", "to": "severity_id" } },
{ "copy": { "from": "unmapped.status_id", "to": "status_id" } },
{ "copy": { "from": "unmapped.status", "to": "status" } },
{ "copy": { "from": "unmapped.message", "to": "message" } },
{ "copy": { "from": "unmapped.user.name", "to": "user.name" } },
{ "copy": { "from": "unmapped.user.account_uid", "to": "user.account_uid" } },
{ "copy": { "from": "unmapped.user.account_type", "to": "user.account_type" } },
{ "copy": { "from": "unmapped.src_endpoint.ip", "to": "src_endpoint.ip" } },
{ "copy": { "from": "unmapped.src_endpoint.location.desc", "to": "src_endpoint.location.desc" } },
{ "copy": { "from": "unmapped.src_endpoint.location.city", "to": "src_endpoint.location.city" } },
{ "copy": { "from": "unmapped.src_endpoint.location.country", "to": "src_endpoint.location.country" } },
{ "copy": { "from": "unmapped.auth_protocol", "to": "auth_protocol" } },
{ "copy": { "from": "unmapped.mfa_factors", "to": "mfa_factors" } }
{
"constant": {
"value": 3002,
"field": "class_uid"
}
},
{
"constant": {
"value": "Authentication",
"field": "class_name"
}
},
{
"constant": {
"value": 3,
"field": "category_uid"
}
},
{
"constant": {
"value": "Identity & Access Management",
"field": "category_name"
}
},
{
"copy": {
"from": "unmapped.activity_id",
"to": "activity_id"
}
},
{
"copy": {
"from": "unmapped.activity_name",
"to": "activity_name"
}
},
{
"copy": {
"from": "unmapped.type_uid",
"to": "type_uid"
}
},
{
"copy": {
"from": "unmapped.severity_id",
"to": "severity_id"
}
},
{
"copy": {
"from": "unmapped.status_id",
"to": "status_id"
}
},
{
"copy": {
"from": "unmapped.status",
"to": "status"
}
},
{
"copy": {
"from": "unmapped.message",
"to": "message"
}
},
{
"copy": {
"from": "unmapped.user.name",
"to": "user.name"
}
},
{
"copy": {
"from": "unmapped.user.account_uid",
"to": "user.account_uid"
}
},
{
"copy": {
"from": "unmapped.user.account_type",
"to": "user.account_type"
}
},
{
"copy": {
"from": "unmapped.src_endpoint.ip",
"to": "src_endpoint.ip"
}
},
{
"copy": {
"from": "unmapped.src_endpoint.location.desc",
"to": "src_endpoint.location.desc"
}
},
{
"copy": {
"from": "unmapped.src_endpoint.location.city",
"to": "src_endpoint.location.city"
}
},
{
"copy": {
"from": "unmapped.src_endpoint.location.country",
"to": "src_endpoint.location.country"
}
},
{
"copy": {
"from": "unmapped.auth_protocol",
"to": "auth_protocol"
}
},
{
"copy": {
"from": "unmapped.auth_protocol_id",
"to": "auth_protocol_id"
}
},
{
"copy": {
"from": "unmapped.mfa_factors",
"to": "mfa_factors"
}
}
]
}
]
},
"observables": {
"fields": [
{
"name": "user.name",
"type": "User"
},
{
"name": "src_endpoint.ip",
"type": "IP Address"
},
{
"name": "auth_protocol",
"type": "Other"
}
]
}
}
+357 -19
View File
@@ -1,25 +1,363 @@
{
attributes: {
dataset: "Endpoint",
"dataSource.name": "CrowdStrike Falcon",
"attributes": {
"dataSource.vendor": "CrowdStrike",
"dataSource.category": "security"
}
patterns: {
keyPattern: "\\w+"
lastValuePattern: "[\\w\\s]+"
"dataSource.name": "CrowdStrike Endpoint",
"dataSource.category": "security",
"metadata.product.vendor_name": "CrowdStrike",
"metadata.product.name": "CrowdStrike Falcon",
"metadata.version": "1.0.0"
},
formats: [
"formats": [
{
format: "CEF:$version$\\|$deviceVendor$\\|$deviceProduct$\\|$deviceVersion$\\|$signatureID$\\|$name$\\|$severity$\\|$extension$"
},
{
format: ".*[\\s]$_=keyPattern$=$_$ \\w+=",
repeat: true
},
{
format: ".*\\s$_=keyPattern$=$_=lastValuePattern$",
repeat: true
"format": "$unmapped.{parse=json}$",
"rewrites": [
{
"input": "unmapped.timestamp",
"output": "timestamp",
"match": ".*",
"replace": "$0"
}
]
}
]
],
"mappings": {
"version": 1,
"mappings": [
{
"predicate": "true",
"transformations": [
{
"constant": {
"value": 1001,
"field": "class_uid"
}
},
{
"constant": {
"value": "Process Activity",
"field": "class_name"
}
},
{
"constant": {
"value": 1,
"field": "category_uid"
}
},
{
"constant": {
"value": "System Activity",
"field": "category_name"
}
},
{
"copy": {
"from": "unmapped.timestamp",
"to": "time"
}
},
{
"replace": {
"field": "time",
"regexp": "(\\d+)\\d{3}",
"replacement": "$1"
}
},
{
"copy": {
"from": "unmapped.event_id",
"to": "metadata.uid"
}
},
{
"copy": {
"from": "unmapped.name",
"to": "message"
}
},
{
"copy": {
"from": "unmapped.event_simpleName",
"to": "activity_name"
}
},
{
"copy": {
"from": "unmapped.ComputerName",
"to": "device.hostname"
}
},
{
"copy": {
"from": "unmapped.aid",
"to": "device.uid"
}
},
{
"copy": {
"from": "unmapped.aip",
"to": "device.ip"
}
},
{
"copy": {
"from": "unmapped.cid",
"to": "device.org.uid"
}
},
{
"copy": {
"from": "unmapped.UserName",
"to": "actor.user.name"
}
},
{
"copy": {
"from": "unmapped.FileName",
"to": "process.file.name"
}
},
{
"copy": {
"from": "unmapped.FilePath",
"to": "process.file.path"
}
},
{
"copy": {
"from": "unmapped.CommandLine",
"to": "process.cmd_line"
}
},
{
"copy": {
"from": "unmapped.ProcessId",
"to": "process.pid"
}
},
{
"copy": {
"from": "unmapped.RawProcessId",
"to": "process.pid"
}
},
{
"copy": {
"from": "unmapped.ParentProcessId",
"to": "process.parent_process.pid"
}
},
{
"copy": {
"from": "unmapped.ParentBaseFileName",
"to": "process.parent_process.file.name"
}
},
{
"copy": {
"from": "unmapped.SHA256HashData",
"to": "process.file.hashes[0].value"
}
},
{
"copy": {
"from": "unmapped.SHA1HashData",
"to": "process.file.hashes[1].value"
}
},
{
"copy": {
"from": "unmapped.MD5HashData",
"to": "process.file.hashes[2].value"
}
},
{
"copy": {
"from": "unmapped.LocalIP",
"to": "src_endpoint.ip"
}
},
{
"copy": {
"from": "unmapped.LocalPort",
"to": "src_endpoint.port"
}
},
{
"copy": {
"from": "unmapped.RemoteIP",
"to": "dst_endpoint.ip"
}
},
{
"copy": {
"from": "unmapped.RemotePort",
"to": "dst_endpoint.port"
}
},
{
"copy": {
"from": "unmapped.Protocol",
"to": "connection_info.protocol_name"
}
},
{
"copy": {
"from": "unmapped.RegObjectName",
"to": "registry.key"
}
},
{
"copy": {
"from": "unmapped.RegValueName",
"to": "registry.value"
}
},
{
"copy": {
"from": "unmapped.DetectName",
"to": "finding.title"
}
},
{
"copy": {
"from": "unmapped.DetectDescription",
"to": "finding.desc"
}
},
{
"copy": {
"from": "unmapped.Severity",
"to": "severity_id"
}
},
{
"copy": {
"from": "unmapped.Tactic",
"to": "finding.supporting_data.tactic"
}
},
{
"copy": {
"from": "unmapped.Technique",
"to": "finding.supporting_data.technique"
}
},
{
"copy": {
"from": "unmapped.IOCType",
"to": "finding.supporting_data.ioc_type"
}
},
{
"copy": {
"from": "unmapped.IOCValue",
"to": "finding.supporting_data.ioc_value"
}
},
{
"copy": {
"from": "unmapped.FalconHostLink",
"to": "metadata.extensions.falcon_link"
}
},
{
"copy": {
"from": "unmapped.SensorId",
"to": "device.uid"
}
},
{
"copy": {
"from": "unmapped.ExternalApiType",
"to": "metadata.extensions.api_type"
}
},
{
"copy": {
"from": "unmapped.PatternDisposition",
"to": "finding.supporting_data.pattern_disposition"
}
},
{
"constant": {
"value": "SHA256",
"field": "process.file.hashes[0].type_id",
"predicate": "unmapped.SHA256HashData != \"\""
}
},
{
"constant": {
"value": "SHA1",
"field": "process.file.hashes[1].type_id",
"predicate": "unmapped.SHA1HashData != \"\""
}
},
{
"constant": {
"value": "MD5",
"field": "process.file.hashes[2].type_id",
"predicate": "unmapped.MD5HashData != \"\""
}
},
{
"constant": {
"value": 1,
"field": "activity_id"
}
},
{
"constant": {
"value": 1,
"field": "status_id"
}
}
]
}
]
},
"observables": {
"fields": [
{
"name": "device.hostname",
"type": "Hostname"
},
{
"name": "actor.user.name",
"type": "User"
},
{
"name": "process.file.name",
"type": "File Name"
},
{
"name": "process.file.path",
"type": "File Name"
},
{
"name": "process.file.hashes[0].value",
"type": "File Hash"
},
{
"name": "process.file.hashes[1].value",
"type": "File Hash"
},
{
"name": "process.file.hashes[2].value",
"type": "File Hash"
},
{
"name": "src_endpoint.ip",
"type": "IP Address"
},
{
"name": "dst_endpoint.ip",
"type": "IP Address"
},
{
"name": "finding.supporting_data.ioc_value",
"type": "Other"
}
]
}
}
+320 -26
View File
@@ -1,33 +1,327 @@
{
attributes: {
"dataSource.category": "security",
"attributes": {
"dataSource.vendor": "Microsoft",
"dataSource.name": "Azure AD",
"dataSource.vendor": "Azure"
"dataSource.category": "security",
"metadata.product.vendor_name": "Microsoft",
"metadata.product.name": "Azure Active Directory",
"metadata.version": "1.0.0"
},
formats: [
"formats": [
{
format: ".*${parse=json}{attrBlacklist=(targetResources)}$"
rewrites: [
{ input: "activityDateTime", output: "security_finding.time_dt", match: ".*", replace: "$0" },
{ input: "activityDisplayName", output: "security_finding.activity_name", match: ".*", replace: "$0" },
{ input: "category", output: "security_finding.category_name", match: ".*", replace: "$0" },
{ input: "correlationId", output: "metadata.correlation_uid", match: ".*", replace: "$0" },
{ input: "id", output: "security_finding.activity_id", match: ".*", replace: "$0" },
{ input: "initiatedByUserId", output: "user.account_uid", match: ".*", replace: "$0" },
{ input: "initiatedByUserIpAddress", output: "user.ip", match: ".*", replace: "$0" },
{ input: "initiatedByUserUserPrincipalName", output: "user.name", match: ".*", replace: "$0" },
{ input: "operationType", output: "security_finding.type_name", match: ".*", replace: "$0" },
{ input: "result", output: "security_finding.result", match: ".*", replace: "$0" },
{ input: "resultReason", output: "security_finding.result_reason", match: ".*", replace: "$0" }
]
}, {
format: ".*targetResources\":..$targetResources.{parse=json}$"
rewrites: [
{ input: "targetResources.displayName", output: "target.name", match: ".*", replace: "$0" },
{ input: "targetResources.id", output: "target.id", match: ".*", replace: "$0" },
{ input: "targetResources.type", output: "target.type", match: ".*", replace: "$0" },
{ input: "targetResources.userPrincipalName", output: "target.userName", match: ".*", replace: "$0" }
"format": "$unmapped.{parse=json}$",
"rewrites": [
{
"input": "unmapped.time",
"output": "timestamp",
"match": ".*",
"replace": "$0"
},
{
"input": "unmapped.activityDateTime",
"output": "timestamp",
"match": ".*",
"replace": "$0"
}
]
}
]
],
"mappings": {
"version": 1,
"mappings": [
{
"predicate": "true",
"transformations": [
{
"constant": {
"value": 3002,
"field": "class_uid"
}
},
{
"constant": {
"value": "Authentication",
"field": "class_name"
}
},
{
"constant": {
"value": 3,
"field": "category_uid"
}
},
{
"constant": {
"value": "Identity & Access Management",
"field": "category_name"
}
},
{
"copy": {
"from": "unmapped.time",
"to": "time"
}
},
{
"copy": {
"from": "unmapped.activityDateTime",
"to": "time"
}
},
{
"cast": {
"field": "time",
"type": "iso8601TimestampToEpochSec"
}
},
{
"copy": {
"from": "unmapped.id",
"to": "metadata.uid"
}
},
{
"copy": {
"from": "unmapped.activityDisplayName",
"to": "message"
}
},
{
"copy": {
"from": "unmapped.activity",
"to": "activity_name"
}
},
{
"copy": {
"from": "unmapped.userPrincipalName",
"to": "user.name"
}
},
{
"copy": {
"from": "unmapped.displayName",
"to": "user.full_name"
}
},
{
"copy": {
"from": "unmapped.userId",
"to": "user.uid"
}
},
{
"copy": {
"from": "unmapped.ipAddress",
"to": "src_endpoint.ip"
}
},
{
"copy": {
"from": "unmapped.clientAppUsed",
"to": "http_request.user_agent"
}
},
{
"copy": {
"from": "unmapped.userAgent",
"to": "http_request.user_agent"
}
},
{
"copy": {
"from": "unmapped.location.city",
"to": "src_endpoint.location.city"
}
},
{
"copy": {
"from": "unmapped.location.state",
"to": "src_endpoint.location.region"
}
},
{
"copy": {
"from": "unmapped.location.countryOrRegion",
"to": "src_endpoint.location.country"
}
},
{
"copy": {
"from": "unmapped.location.geoCoordinates.latitude",
"to": "src_endpoint.location.coordinates[0]"
}
},
{
"copy": {
"from": "unmapped.location.geoCoordinates.longitude",
"to": "src_endpoint.location.coordinates[1]"
}
},
{
"copy": {
"from": "unmapped.result",
"to": "status"
}
},
{
"copy": {
"from": "unmapped.resultReason",
"to": "status_detail"
}
},
{
"copy": {
"from": "unmapped.operationType",
"to": "activity_name"
}
},
{
"copy": {
"from": "unmapped.category",
"to": "category_name"
}
},
{
"copy": {
"from": "unmapped.correlationId",
"to": "metadata.correlation_uid"
}
},
{
"copy": {
"from": "unmapped.resourceDisplayName",
"to": "dst_endpoint.name"
}
},
{
"copy": {
"from": "unmapped.resourceId",
"to": "dst_endpoint.uid"
}
},
{
"copy": {
"from": "unmapped.targetResources[0].displayName",
"to": "dst_endpoint.name"
}
},
{
"copy": {
"from": "unmapped.targetResources[0].id",
"to": "dst_endpoint.uid"
}
},
{
"copy": {
"from": "unmapped.targetResources[0].userPrincipalName",
"to": "dst_endpoint.name"
}
},
{
"copy": {
"from": "unmapped.authenticationDetails[0].authenticationMethod",
"to": "auth_protocol"
}
},
{
"copy": {
"from": "unmapped.authenticationDetails[0].succeeded",
"to": "status"
}
},
{
"copy": {
"from": "unmapped.conditionalAccessStatus",
"to": "metadata.extensions.conditional_access_status"
}
},
{
"copy": {
"from": "unmapped.isInteractive",
"to": "metadata.extensions.is_interactive"
}
},
{
"copy": {
"from": "unmapped.riskLevel",
"to": "risk_level"
}
},
{
"copy": {
"from": "unmapped.riskState",
"to": "risk_level_id"
}
},
{
"constant": {
"value": 1,
"field": "activity_id",
"predicate": "unmapped.result = 'success'"
}
},
{
"constant": {
"value": 2,
"field": "activity_id",
"predicate": "unmapped.result = 'failure'"
}
},
{
"constant": {
"value": 1,
"field": "severity_id",
"predicate": "unmapped.result = 'success'"
}
},
{
"constant": {
"value": 3,
"field": "severity_id",
"predicate": "unmapped.result = 'failure'"
}
},
{
"constant": {
"value": 1,
"field": "status_id",
"predicate": "unmapped.result = 'success'"
}
},
{
"constant": {
"value": 2,
"field": "status_id",
"predicate": "unmapped.result = 'failure'"
}
}
]
}
]
},
"observables": {
"fields": [
{
"name": "user.name",
"type": "User"
},
{
"name": "src_endpoint.ip",
"type": "IP Address"
},
{
"name": "user.uid",
"type": "User"
},
{
"name": "dst_endpoint.name",
"type": "Other"
},
{
"name": "metadata.correlation_uid",
"type": "Other"
}
]
}
}
+409 -45
View File
@@ -1,46 +1,410 @@
{
attributes: {
"dataSource.category": "security",
"dataSource.name": "Netskope",
"dataSource.vendor": "Netskope"
},
formats: [
{
format: ".*${parse=json}$"
rewrites: [
{ input: "_category_id", output: "security_finding.category_uid", match: ".*", replace: "$0" },
{ input: "_correlation_id", output: "metadata.correlation_uid", match: ".*", replace: "$0" },
{ input: "_detection_name", output: "detection.name", match: ".*", replace: "$0" },
{ input: "_event_id", output: "security_finding.activity_id", match: ".*", replace: "$0" },
{ input: "_id", output: "security_finding.type_uid", match: ".*", replace: "$0" },
{ input: "_nshostname", output: "network_endpoint.sender_hostname", match: ".*", replace: "$0" },
{ input: "_resource_name", output: "resource.name", match: ".*", replace: "$0" },
{ input: "account_name", output: "account.name", match: ".*", replace: "$0" },
{ input: "action", output: "security_finding.action", match: ".*", replace: "$0" },
{ input: "alert_id", output: "alert.uid", match: ".*", replace: "$0" },
{ input: "alert_name", output: "alert.name", match: ".*", replace: "$0" },
{ input: "alert_type", output: "event.type", match: ".*", replace: "$0" },
{ input: "device", output: "device.name", match: ".*", replace: "$0" },
{ input: "dlp_file", output: "dlp.file_name", match: ".*", replace: "$0" },
{ input: "dlp_incident_id", output: "dlp.incident_id", match: ".*", replace: "$0" },
{ input: "dlp_rule", output: "dlp.rule", match: ".*", replace: "$0" },
{ input: "dstip", output: "dst.ip.address", match: ".*", replace: "$0" },
{ input: "file_name", output: "file.name", match: ".*", replace: "$0" },
{ input: "file_size", output: "file.size", match: ".*", replace: "$0" },
{ input: "file_type", output: "file.type", match: ".*", replace: "$0" },
{ input: "hostname", output: "device.hostname", match: ".*", replace: "$0" },
{ input: "malware_name", output: "malware.name", match: ".*", replace: "$0" },
{ input: "md5", output: "file.md5", match: ".*", replace: "$0" },
{ input: "os", output: "os.name", match: ".*", replace: "$0" },
{ input: "policy", output: "policy.name", match: ".*", replace: "$0" },
{ input: "policy_id", output: "policy.uid", match: ".*", replace: "$0" },
{ input: "protocol", output: "network_connection_info.protocol_name", match: ".*", replace: "$0" },
{ input: "srcip", output: "src.ip.address", match: ".*", replace: "$0" },
{ input: "url", output: "url.text", match: ".*", replace: "$0" },
{ input: "user", output: "user.name", match: ".*", replace: "$0" },
{ input: "user_id", output: "user.uid", match: ".*", replace: "$0" },
{ input: "userip", output: "user.ip", match: ".*", replace: "$0" }
]
}
]
}
attributes: {
"dataSource.category": "security",
"dataSource.name": "Netskope",
"dataSource.vendor": "Netskope"
},
formats: [
{
format: ".*${parse=json}{attrWhitelist=(_id|action|activity|device|category|app_name|count|dst_country|dst_region|malware_severity|malware_type|request_id|severity|severity_id|src_country|src_location|src_region|type|_category_id|_category_tags|_correlation_id|_detection_name|_event_id|_nshostname|_resource_name|_service_identifier|account_name|alert_id|alert_name|alert_type|appcategory|breach_date|breach_description|breach_id|breach_score|connection_id|dlp_file|dlp_incident_id|dlp_rule|dlp_rule_count|dst_latitude|dst_longitude|dst_timezone|dst_zipcode|dstip|event_type|file_name|file_size|file_type|hostname|incident_id|instance_id|local_md5|malsite_country|malware_name|matched_username|md5|os|os10|os11|policy|policy_id|protocol|severity_level|severity_level_id|src_latitude|src_longitude|src_time|src_timezone|src_zipcode|srcip|timestamp|transaction_id|true_obj_type|url|user|user_id|userip|userkey|local_sha256)}$"
rewrites: [
{
input: "_category_id",
output: "security_finding.category_uid",
match: ".*",
replace: "$0"
},
{
input: "_category_tags",
output: "security_finding.category_tags",
match: ".*",
replace: "$0"
},
{
input: "_correlation_id",
output: "metadata.correlation_uid",
match: ".*",
replace: "$0"
},
{
input: "_detection_name",
output: "detection.name",
match: ".*",
replace: "$0"
},
{
input: "_event_id",
output: "security_finding.activity_id",
match: ".*",
replace: "$0"
},
{
input: "_id",
output: "security_finding.type_uid",
match: ".*",
replace: "$0"
},
{
input: "_nshostname",
output: "network_endpoint.sender_hostname",
match: ".*",
replace: "$0"
},
{
input: "_resource_name",
output: "resource.name",
match: ".*",
replace: "$0"
},
{
input: "_service_identifier",
output: "service.identifier",
match: ".*",
replace: "$0"
},
{
input: "account_name",
output: "account.name",
match: ".*",
replace: "$0"
},
{
input: "action",
output: "security_finding.action",
match: ".*",
replace: "$0"
},
{
input: "alert_id",
output: "alert.uid",
match: ".*",
replace: "$0"
},
{
input: "alert_name",
output: "alert.name",
match: ".*",
replace: "$0"
},
{
input: "alert_type",
output: "event.type",
match: ".*",
replace: "$0"
},
{
input: "appcategory",
output: "security_finding.app_category",
match: ".*",
replace: "$0"
},
{
input: "breach_date",
output: "breach.date",
match: ".*",
replace: "$0"
},
{
input: "breach_description",
output: "breach.desc",
match: ".*",
replace: "$0"
},
{
input: "breach_id",
output: "breach.uid",
match: ".*",
replace: "$0"
},
{
input: "breach_score",
output: "breach.score",
match: ".*",
replace: "$0"
},
{
input: "category",
output: "security_finding.category_name",
match: ".*",
replace: "$0"
},
{
input: "connection_id",
output: "security_finding.connection_id",
match: ".*",
replace: "$0"
},
{
input: "device",
output: "device.name",
match: ".*",
replace: "$0"
},
{
input: "dlp_file",
output: "dlp.file_name",
match: ".*",
replace: "$0"
},
{
input: "dlp_incident_id",
output: "dlp.incident_id",
match: ".*",
replace: "$0"
},
{
input: "dlp_rule",
output: "dlp.rule",
match: ".*",
replace: "$0"
},
{
input: "dlp_rule_count",
output: "dlp.rule_count",
match: ".*",
replace: "$0"
},
{
input: "dst_latitude",
output: "geo_coordinates.dst_latitude",
match: ".*",
replace: "$0"
},
{
input: "dst_longitude",
output: "geo_coordinates.dst_longitude",
match: ".*",
replace: "$0"
},
{
input: "dst_timezone",
output: "location.dst_timezone",
match: ".*",
replace: "$0"
},
{
input: "dst_zipcode",
output: "location.dst_zipcode",
match: ".*",
replace: "$0"
},
{
input: "dstip",
output: "dst.ip.address",
match: ".*",
replace: "$0"
},
{
input: "event_type",
output: "security_finding.ref_event_name",
match: ".*",
replace: "$0"
},
{
input: "file_name",
output: "file.name",
match: ".*",
replace: "$0"
},
{
input: "file_size",
output: "file.size",
match: ".*",
replace: "$0"
},
{
input: "file_type",
output: "file.type",
match: ".*",
replace: "$0"
},
{
input: "hostname",
output: "device.hostname",
match: ".*",
replace: "$0"
},
{
input: "incident_id",
output: "security_finding.incident_id",
match: ".*",
replace: "$0"
},
{
input: "instance_id",
output: "device.instance_uid",
match: ".*",
replace: "$0"
},
{
input: "local_md5",
output: "file.local_md5",
match: ".*",
replace: "$0"
},
{
input: "malsite_country",
output: "security_finding.malsite_country",
match: ".*",
replace: "$0"
},
{
input: "malware_name",
output: "malware.name",
match: ".*",
replace: "$0"
},
{
input: "matched_username",
output: "security_finding.matched_username",
match: ".*",
replace: "$0"
},
{
input: "md5",
output: "file.md5",
match: ".*",
replace: "$0"
},
{
input: "os",
output: "os.name",
match: ".*",
replace: "$0"
},
{
input: "os10",
output: "device.os10",
match: ".*",
replace: "$0"
},
{
input: "os11",
output: "device.os11",
match: ".*",
replace: "$0"
},
{
input: "policy",
output: "policy.name",
match: ".*",
replace: "$0"
},
{
input: "policy_id ",
output: "policy.uid",
match: ".*",
replace: "$0"
},
{
input: "protocol",
output: "network_connection_info.protocol_name",
match: ".*",
replace: "$0"
},
{
input: "severity_level",
output: "security_finding.severity",
match: ".*",
replace: "$0"
},
{
input: "severity_level_id",
output: "security_finding.severity_level_id",
match: ".*",
replace: "$0"
},
{
input: "src_latitude",
output: "geo_coordinates.src_latitude",
match: ".*",
replace: "$0"
},
{
input: "src_longitude",
output: "geo_coordinates.src_longitude",
match: ".*",
replace: "$0"
},
{
input: "src_time",
output: "security_finding.src_time",
match: ".*",
replace: "$0"
},
{
input: "src_timezone",
output: "location.src_timezone",
match: ".*",
replace: "$0"
},
{
input: "src_zipcode",
output: "location.src_zipcode",
match: ".*",
replace: "$0"
},
{
input: "srcip",
output: "src.ip.address",
match: ".*",
replace: "$0"
},
{
input: "timestamp",
output: "security_finding.time",
match: ".*",
replace: "$0"
},
{
input: "transaction_id",
output: "security_finding.transaction_id",
match: ".*",
replace: "$0"
},
{
input: "true_obj_type",
output: "file.type",
match: ".*",
replace: "$0"
},
{
input: "url",
output: "url.text",
match: ".*",
replace: "$0"
},
{
input: "user",
output: "user.name",
match: ".*",
replace: "$0"
},
{
input: "user_id",
output: "user.uid",
match: ".*",
replace: "$0"
},
{
input: "userip",
output: "user.ip",
match: ".*",
replace: "$0"
},
{
input: "userkey",
output: "user.key",
match: ".*",
replace: "$0"
},
{
input: "local_sha256",
output: "file.local_sha256",
match: ".*",
replace: "$0"
},
]
}
]
}
+284 -32
View File
@@ -1,39 +1,291 @@
{
attributes: {
source: "okta"
"dataSource.category": "security",
"dataSource.name": "Okta",
"attributes": {
"dataSource.vendor": "Okta",
"dataSource.name": "Okta System Log",
"dataSource.category": "security",
"metadata.product.vendor_name": "Okta",
"metadata.product.name": "Okta System Log",
"metadata.version": "1.0.0"
},
formats: [
"formats": [
{
format: ".*${parse=dottedJson}{attrBlacklist=target}$"
rewrites: [
{ input: "actor.id", output: "user.account_uid", match: ".*", replace: "$0" },
{ input: "actor.type", output: "user.account_type", match: ".*", replace: "$0" },
{ input: "actor.alternateId", output: "user.email_addr", match: ".*", replace: "$0" },
{ input: "actor.displayName", output: "user.name", match: ".*", replace: "$0" },
{ input: "authenticationContext.authenticationStep", output: "authenticationStep", match: ".*", replace: "$0" },
{ input: "authenticationContext.externalSessionId", output: "externalSessionId", match: ".*", replace: "$0" },
{ input: "client.ipAddress", output: "client.ip", match: ".*", replace: "$0" },
{ input: "client.userAgent.browser", output: "client.browser", match: ".*", replace: "$0" },
{ input: "client.userAgent.os", output: "client.os", match: ".*", replace: "$0" },
{ input: "client.userAgent.rawUserAgent", output: "client.userAgent", match: ".*", replace: "$0" },
{ input: "client.zone", output: "client.location.zone", match: ".*", replace: "$0" },
{ input: "client.geographicalContext.city", output: "client.location.city", match: ".*", replace: "$0" },
{ input: "client.geographicalContext.country", output: "client.location.country", match: ".*", replace: "$0" },
{ input: "client.geographicalContext.geolocation.lat", output: "client.location.lat", match: ".*", replace: "$0" },
{ input: "client.geographicalContext.geolocation.lon", output: "client.location.lon", match: ".*", replace: "$0" },
{ input: "client.geographicalContext.postalCode", output: "client.location.postal_code", match: ".*", replace: "$0" },
{ input: "client.geographicalContext.state", output: "client.location.state", match: ".*", replace: "$0" },
{ input: "displayMessage", output: "msg", match: ".*", replace: "$0" },
{ input: "eventType", output: "category_name", match: ".*", replace: "$0" },
{ input: "outcome.result", output: "result", match: ".*", replace: "$0" },
{ input: "published", output: "time", match: ".*", replace: "$0" },
{ input: "transaction.id", output: "type_uid", match: ".*", replace: "$0" },
{ input: "transaction.type", output: "type_name", match: ".*", replace: "$0" },
{ input: "uuid", output: "activity_id", match: ".*", replace: "$0" }
"format": "$unmapped.{parse=json}$",
"rewrites": [
{
"input": "unmapped.published",
"output": "timestamp",
"match": ".*",
"replace": "$0"
}
]
}
]
],
"mappings": {
"version": 1,
"mappings": [
{
"predicate": "true",
"transformations": [
{
"constant": {
"value": 3002,
"field": "class_uid"
}
},
{
"constant": {
"value": "Authentication",
"field": "class_name"
}
},
{
"constant": {
"value": 3,
"field": "category_uid"
}
},
{
"constant": {
"value": "Identity & Access Management",
"field": "category_name"
}
},
{
"copy": {
"from": "unmapped.published",
"to": "time"
}
},
{
"cast": {
"field": "time",
"type": "iso8601TimestampToEpochSec"
}
},
{
"copy": {
"from": "unmapped.uuid",
"to": "metadata.uid"
}
},
{
"copy": {
"from": "unmapped.eventType",
"to": "type_name"
}
},
{
"copy": {
"from": "unmapped.displayMessage",
"to": "message"
}
},
{
"copy": {
"from": "unmapped.actor.alternateId",
"to": "user.name"
}
},
{
"copy": {
"from": "unmapped.actor.displayName",
"to": "user.full_name"
}
},
{
"copy": {
"from": "unmapped.actor.id",
"to": "user.uid"
}
},
{
"copy": {
"from": "unmapped.client.ipAddress",
"to": "src_endpoint.ip"
}
},
{
"copy": {
"from": "unmapped.client.geographicalContext.city",
"to": "src_endpoint.location.city"
}
},
{
"copy": {
"from": "unmapped.client.geographicalContext.state",
"to": "src_endpoint.location.region"
}
},
{
"copy": {
"from": "unmapped.client.geographicalContext.country",
"to": "src_endpoint.location.country"
}
},
{
"copy": {
"from": "unmapped.client.geographicalContext.postalCode",
"to": "src_endpoint.location.postal_code"
}
},
{
"copy": {
"from": "unmapped.client.geographicalContext.geolocation.lat",
"to": "src_endpoint.location.coordinates[0]"
}
},
{
"copy": {
"from": "unmapped.client.geographicalContext.geolocation.lon",
"to": "src_endpoint.location.coordinates[1]"
}
},
{
"copy": {
"from": "unmapped.client.userAgent.rawUserAgent",
"to": "http_request.user_agent"
}
},
{
"copy": {
"from": "unmapped.client.userAgent.os.family",
"to": "src_endpoint.os.name"
}
},
{
"copy": {
"from": "unmapped.client.userAgent.browser.family",
"to": "http_request.user_agent"
}
},
{
"copy": {
"from": "unmapped.outcome.result",
"to": "status"
}
},
{
"copy": {
"from": "unmapped.outcome.reason",
"to": "status_detail"
}
},
{
"copy": {
"from": "unmapped.transaction.type",
"to": "auth_protocol"
}
},
{
"copy": {
"from": "unmapped.transaction.id",
"to": "session.uid"
}
},
{
"copy": {
"from": "unmapped.authenticationContext.externalSessionId",
"to": "session.uid"
}
},
{
"copy": {
"from": "unmapped.debugContext.debugData.requestId",
"to": "metadata.correlation_uid"
}
},
{
"copy": {
"from": "unmapped.debugContext.debugData.requestUri",
"to": "http_request.url.path"
}
},
{
"copy": {
"from": "unmapped.target[0].displayName",
"to": "dst_endpoint.name"
}
},
{
"copy": {
"from": "unmapped.target[0].alternateId",
"to": "dst_endpoint.uid"
}
},
{
"copy": {
"from": "unmapped.severity",
"to": "severity"
}
},
{
"constant": {
"value": 1,
"field": "activity_id",
"predicate": "unmapped.outcome.result = 'SUCCESS'"
}
},
{
"constant": {
"value": 2,
"field": "activity_id",
"predicate": "unmapped.outcome.result = 'FAILURE'"
}
},
{
"constant": {
"value": 1,
"field": "severity_id",
"predicate": "unmapped.outcome.result = 'SUCCESS'"
}
},
{
"constant": {
"value": 3,
"field": "severity_id",
"predicate": "unmapped.outcome.result = 'FAILURE'"
}
},
{
"constant": {
"value": 1,
"field": "status_id",
"predicate": "unmapped.outcome.result = 'SUCCESS'"
}
},
{
"constant": {
"value": 2,
"field": "status_id",
"predicate": "unmapped.outcome.result = 'FAILURE'"
}
}
]
}
]
},
"observables": {
"fields": [
{
"name": "user.name",
"type": "User"
},
{
"name": "src_endpoint.ip",
"type": "IP Address"
},
{
"name": "http_request.user_agent",
"type": "Other"
},
{
"name": "session.uid",
"type": "Other"
},
{
"name": "dst_endpoint.name",
"type": "Other"
}
]
}
}
+86 -8
View File
@@ -1,13 +1,91 @@
{
attributes: {
"dataSource.vendor": "Palo Alto Networks",
"dataSource.name": "Palo Alto Networks Prisma SASE",
"dataSource.category": "security",
// specify a time zone if the timestamps in your log are not in GMT
//timezone: "Europe/Prague"
attributes: { "dataset.technology":"firewall", "dataset.vendor":"palo_alto", "dataset.app":"palo_alto" }
patterns: {
//maps to high_resolution_timestamp:
// timestamp: "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}(\\+|-)\\d{2}:\\d{2}"
tsPatternPA: "[A-za-z]+\\s+\\d{1,2} [\\d:]+"
//application_characteristic can be a single value, a comma delimited list in quotes, or blank. Null value is handled by format: traffic-2, not by this pattern.
application_characteristic: "(\".*\")|[^,]+"
//description field from system log is wrapped in quotes and may contain commas"
description: "(\".*\")"
//discard future_use fields
misc: "[^,]*"
}
formats: [
//change pattern depending on the timestamp fomat
{
format: "$network_traffic.log_header$,$network_traffic.log_source_uid$,$event.type$,$network_traffic.sub_type_value$,$network_traffic.config_version_value$,$network_activity.time$,$src.ip.address$,$dst.ip.address$,$nat_src.device_ip$,$nat_dst.device_ip$,$rule.name$,$user.src_name$,$user.dst_name$,$network_traffic.app_name$,$network_traffic.virtual_system_location$,$network_traffic.from_zone$,$network_traffic.to_zone$,$network_traffic.inbound_if_value$,$network_traffic.outbound_if_value$,$network_traffic.log_set$,$session.uid$,$network_traffic.repeat_count$,$network_endpoint.src_port$,$network_endpoint.dst_port$,$nat_src.port$,$nat_dst.port$,$network_traffic.flags$,$network_connection_info.protocol_name$,$network_traffic.bytes$,$network_traffic.bytes_out$,$network_traffic.bytes_in$,$network_traffic.packets$,$session.created_time$,$network_traffic.total_time_elapsed$,$url.categories$,$metadata.sequence$,$network_traffic.action_flags$,$location.src_region$,$location.dst_region$,$network_traffic.packets_out$,$network_traffic.packets_in$,$network_traffic.session_end_reason_value$,$network_traffic.dg_hier_level_1$,$network_traffic.dg_hier_level_2$,$network_traffic.dg_hier_level_3$,$network_traffic.dg_hier_level_4$,$network_traffic.virtual_system_name$,$endpoint.name$,$network_traffic.action_source_value$,$source.uuid$,$destination.uuid$,$network_traffic.tunnel_id_imsi$,$network_traffic.monitor_tag_imei$,$session.parent_id$,$session.parent_start_time$,$network_traffic.tunnel_value$,$network_traffic.ep_association_uid$,$network_traffic.chunks$,$network_traffic.chunks_out$,$network_traffic.chunks_in$,$rule.uid$,$network_traffic.http2_connection$,$network_traffic.link_change_count$,$policy.uid$,$network_traffic.link_switches$,$network_traffic.sdwan_cluster$,$network_traffic.sdwan_device_type$,$network_traffic.sdwan_cluster_type$,$network_traffic.sdwan_site$,$network_traffic.dynusergroup_name$,$http_request.x_forwarded_for$,$source_device.category_name$,$source_device.profile$,$source_device.model$,$source_device.vendor_name$,$source_device.os_name$,$source_device.os_version$,$source_device.hostname$,$source_device.mac$,$destination_device.category_name$,$destination_device.profile$,$destination_device.model$,$destination_device.vendor_name$,$destination_device.os_name$,$destination_device.os_version$,$destination_device.hostname$,$destination_device.mac$,$container.uid$,$network_traffic.pod_namespace$,$network_traffic.pod_name$,$network_traffic.source_edl$,$network_traffic.destination_edl$,$host.uid$,$endpoint.serial_number$,$network_traffic.source_dynamic_address_group$,$network_traffic.destination_dynamic_address_group$,$network_traffic.ha_session_owner$,$network_traffic.timestamp_generated_high_res$,$network_traffic.nssai_network_slice_type_value$,$network_traffic.nssai_network_slice_differentiator_value$",
halt: true,
}
format: ".*$timestamp=tsPatternPA$(\\,)*",
},
{
//match all fields. application_characteristic can be a single value, or a comma delimited list in quotes.
id: "traffic-1",
attributes: { type: "TRAFFIC", format: "traffic-1" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$firewall_serial_number$,TRAFFIC,$sub_type$,\\d+,$generate_time$,$source_address$,$destination_address$,$source_nat_address$,$destination_nat_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$time_stamp$,$session_id$,$repeat_count$,$source_port$,$destination_port$,$source_nat_port$,$destination_nat_port$,$flag$,$protocol$,$action$,$bytes$,$bytes_sent$,$bytes_received$,$packets$,$start_time$,$elapsed_time$,$category$,$test$,$sequence_number$,$action_flags$,$source_country$,$destination_country$,,$packets_sent$,$packets_received$,$session_end_reason$,\\d+,\\d+,\\d+,\\d+,$virtual_system_name$,$device_name$,$action_source$,$source_vm_uuid$,$destination_vm_uuid$,$tunnel_id_imsi$,$monitor_tag_imei$,$parent_session_id$,$parent_start_time$,$tunnel_type$,$sctp_association_id$,$sctp_chunks$,$sctp_chunks_sent$,$sctp_chunks_received$,$rule_uuid$,$http_2_connection$,$app_flap_count$,$policy_id$,$link_switches$,$sd-wan_cluster$,$sd-wan_device_type$,$sd-wan_cluster_type$,$sd-wan_site$,$dynamic_user_group_name$,$xff_address$,$source_device_category$,$source_device_profile$,$source_device_model$,$source_device_vendor$,$source_device_os_family$,$source_device_os_version$,$source_hostname$,$source_mac_address$,$destination_device_category$,$destination_device_profile$,$destination_device_model$,$destination_device_vendor$,$destination_device_os_family$,$destination_device_os_version$,$destination_hostname$,$destination_mac_address$,$container_id$,$pod_namespace$,$pod_name$,$source_external_dynamic_list$,$destination_external_dynamic_list$,$host_id$,$serial_number$,$source_dynamic_address_group$,$destination_dynamic_address_group$,$session_owner$,$high_resolution_timestamp$,$a_slice_service_type$,$a_slice_differentiator$,$application_subcategory$,$application_category$,$application_technology$,$application_risk$,$application_characteristic=application_characteristic$,$application_container$,$tunneled_application$,$application_saas$,$application_sanctioned_state$,$offloaded$",
halt: true
},
{
//dont match on application_characteristic for cases where is it blank.
id: "traffic-2",
attributes: { type: "TRAFFIC", format: "traffic-2" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$firewall_serial_number$,TRAFFIC,$sub_type$,\\d+,$generate_time$,$source_address$,$destination_address$,$source_nat_address$,$destination_nat_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$time_stamp$,$session_id$,$repeat_count$,$source_port$,$destination_port$,$source_nat_port$,$destination_nat_port$,$flag$,$protocol$,$action$,$bytes$,$bytes_sent$,$bytes_received$,$packets$,$start_time$,$elapsed_time$,$category$,$test$,$sequence_number$,$action_flags$,$source_country$,$destination_country$,,$packets_sent$,$packets_received$,$session_end_reason$,\\d+,\\d+,\\d+,\\d+,$virtual_system_name$,$device_name$,$action_source$,$source_vm_uuid$,$destination_vm_uuid$,$tunnel_id_imsi$,$monitor_tag_imei$,$parent_session_id$,$parent_start_time$,$tunnel_type$,$sctp_association_id$,$sctp_chunks$,$sctp_chunks_sent$,$sctp_chunks_received$,$rule_uuid$,$http_2_connection$,$app_flap_count$,$policy_id$,$link_switches$,$sd-wan_cluster$,$sd-wan_device_type$,$sd-wan_cluster_type$,$sd-wan_site$,$dynamic_user_group_name$,$xff_address$,$source_device_category$,$source_device_profile$,$source_device_model$,$source_device_vendor$,$source_device_os_family$,$source_device_os_version$,$source_hostname$,$source_mac_address$,$destination_device_category$,$destination_device_profile$,$destination_device_model$,$destination_device_vendor$,$destination_device_os_family$,$destination_device_os_version$,$destination_hostname$,$destination_mac_address$,$container_id$,$pod_namespace$,$pod_name$,$source_external_dynamic_list$,$destination_external_dynamic_list$,$host_id$,$serial_number$,$source_dynamic_address_group$,$destination_dynamic_address_group$,$session_owner$,$high_resolution_timestamp$,$a_slice_service_type$,$a_slice_differentiator$,$application_subcategory$,$application_category$,$application_technology$,$application_risk$,,$application_container$,$tunneled_application$,$application_saas$,$application_sanctioned_state$,$offloaded$",
halt: true
},
{
id: "system",
attributes: { type: "SYSTEM", format: "system" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$serial_number$,SYSTEM,$content_threat_type$,.*,$generated_time$,$virtual_system$,$event_id$,$object$,.*,.*,$module$,$severity$,$description=description$,$sequence_number$,$action_flags$,$device_group_hierarchy_level_1$,$device_group_hierarchy_level_2$,$device_group_hierarchy_level_3$,$device_group_hierarchy_level_4$,$virtual_system_name$,$device_name$,.*,.*,$high_resolution_timestamp$",
halt: true
},
{
//matches THREAT logs with comma surround lists in application_characteristic and url_category_list.
// Matches THREAT logs with commas surrounding user_agent
//PAN OS 10.2 will add $cloud_report_id to the end of this log format. If this no longer matches threat logs, check for the extra field at the end.
id: "threat-0",
attributes: { type: "THREAT", format: "threat-0" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$serial_number$,THREAT,$threat_content_type$,\\d+,$Generated_time$,$source_address$,$destination_address$,$nat_source_address$,$nat_destination_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$misc=misc$,$Session_id$,$repeat_count$,$source_port$,$destination_port$,$nat_source_port$,$nat_destination_port$,$flags$,$ip_protocol$,$action$,$url_filename$,$threat_id$,$category$,$severity$,$direction$,$sequence_number$,$action_flags$,$source_location$,$destination_location$,$misc=misc$,$Content_type$,$pcap_id$,$file_digest$,$cloud$,$url_index$,\"$user_agent$\",$file_type$,$x-forwarded-for$,$referer$,$sender$,$subject$,$recipient$,$report_id$,$device_group_hierarchy_level_1$,$device_group_hierarchy_level_2$,$device_group_hierarchy_level_3$,$device_group_hierarchy_level_4$,$virtual_system_name$,$device_name$,$misc=misc$,$Source_vm_uuid$,$destination_vm_uuid$,$http_method$,$tunnel_id_imsi$,$monitor_tag_imei$,$parent_session_id$,$parent_start_time$,$tunnel_type$,$threat_category$,$content_version$,$misc=misc$,$Sctp_association_id$,$payload_protocol_id$,$http_headers$,\"$url_category_list$\",$rule_uuid$,$http_2_connection$,$dynamic_user_group_name$,$xff_address$,$source_device_category$,$source_device_profile$,$source_device_model$,$source_device_vendor$,$source_device_os_family$,$source_device_os_version$,$source_hostname$,$source_mac_address$,$destination_device_category$,$destination_device_profile$,$destination_device_model$,$destination_device_vendor$,$destination_device_os_family$,$destination_device_os_version$,$destination_hostname$,$destination_mac_address$,$container_id$,$pod_namespace$,$pod_name$,$source_external_dynamic_list$,$destination_external_dynamic_list$,$host_id$,$serial_number$,$domain_edl$,$source_dynamic_address_group$,$destination_dynamic_address_group$,$partial_hash$,$high_resolution_timestamp$,$reason$,$justification$,$a_slice_service_type$,$application_subcategory$,$application_category$,$application_technology$,$application_risk$,\"$application_characteristic$\",$application_container$,$tunneled_application$,$application_saas$,$application_sanctioned_state$",
halt: true
},
{
//matches THREAT logs with comma surround lists in application_characteristic and url_category_list.
//PAN OS 10.2 will add $cloud_report_id to the end of this log format. If this no longer matches threat logs, check for the extra field at the end.
id: "threat-1",
attributes: { type: "THREAT", format: "threat-1" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$serial_number$,THREAT,$threat_content_type$,\\d+,$Generated_time$,$source_address$,$destination_address$,$nat_source_address$,$nat_destination_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$misc=misc$,$Session_id$,$repeat_count$,$source_port$,$destination_port$,$nat_source_port$,$nat_destination_port$,$flags$,$ip_protocol$,$action$,$url_filename$,$threat_id$,$category$,$severity$,$direction$,$sequence_number$,$action_flags$,$source_location$,$destination_location$,$misc=misc$,$Content_type$,$pcap_id$,$file_digest$,$cloud$,$url_index$,$user_agent$,$file_type$,$x-forwarded-for$,$referer$,$sender$,$subject$,$recipient$,$report_id$,$device_group_hierarchy_level_1$,$device_group_hierarchy_level_2$,$device_group_hierarchy_level_3$,$device_group_hierarchy_level_4$,$virtual_system_name$,$device_name$,$misc=misc$,$Source_vm_uuid$,$destination_vm_uuid$,$http_method$,$tunnel_id_imsi$,$monitor_tag_imei$,$parent_session_id$,$parent_start_time$,$tunnel_type$,$threat_category$,$content_version$,$misc=misc$,$Sctp_association_id$,$payload_protocol_id$,$http_headers$,\"$url_category_list$\",$rule_uuid$,$http_2_connection$,$dynamic_user_group_name$,$xff_address$,$source_device_category$,$source_device_profile$,$source_device_model$,$source_device_vendor$,$source_device_os_family$,$source_device_os_version$,$source_hostname$,$source_mac_address$,$destination_device_category$,$destination_device_profile$,$destination_device_model$,$destination_device_vendor$,$destination_device_os_family$,$destination_device_os_version$,$destination_hostname$,$destination_mac_address$,$container_id$,$pod_namespace$,$pod_name$,$source_external_dynamic_list$,$destination_external_dynamic_list$,$host_id$,$serial_number$,$domain_edl$,$source_dynamic_address_group$,$destination_dynamic_address_group$,$partial_hash$,$high_resolution_timestamp$,$reason$,$justification$,$a_slice_service_type$,$application_subcategory$,$application_category$,$application_technology$,$application_risk$,\"$application_characteristic$\",$application_container$,$tunneled_application$,$application_saas$,$application_sanctioned_state$",
halt: true
},
{
//matches THREAT logs without comma surround list in url_category_list.
//PAN OS 10.2 will add $cloud_report_id to the end of this log format. If this no longer matches threat logs, check for the extra field at the end.
id: "threat-2",
attributes: { type: "THREAT", format: "threat-2" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$serial_number$,THREAT,$threat_content_type$,\\d+,$Generated_time$,$source_address$,$destination_address$,$nat_source_address$,$nat_destination_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$misc=misc$,$Session_id$,$repeat_count$,$source_port$,$destination_port$,$nat_source_port$,$nat_destination_port$,$flags$,$ip_protocol$,$action$,$url_filename$,$threat_id$,$category$,$severity$,$direction$,$sequence_number$,$action_flags$,$source_location$,$destination_location$,$misc=misc$,$Content_type$,$pcap_id$,$file_digest$,$cloud$,$url_index$,$user_agent$,$file_type$,$x-forwarded-for$,$referer$,$sender$,$subject$,$recipient$,$report_id$,$device_group_hierarchy_level_1$,$device_group_hierarchy_level_2$,$device_group_hierarchy_level_3$,$device_group_hierarchy_level_4$,$virtual_system_name$,$device_name$,$misc=misc$,$Source_vm_uuid$,$destination_vm_uuid$,$http_method$,$tunnel_id_imsi$,$monitor_tag_imei$,$parent_session_id$,$parent_start_time$,$tunnel_type$,$threat_category$,$content_version$,$misc=misc$,$Sctp_association_id$,$payload_protocol_id$,$http_headers$,$url_category_list$,$rule_uuid$,$http_2_connection$,$dynamic_user_group_name$,$xff_address$,$source_device_category$,$source_device_profile$,$source_device_model$,$source_device_vendor$,$source_device_os_family$,$source_device_os_version$,$source_hostname$,$source_mac_address$,$destination_device_category$,$destination_device_profile$,$destination_device_model$,$destination_device_vendor$,$destination_device_os_family$,$destination_device_os_version$,$destination_hostname$,$destination_mac_address$,$container_id$,$pod_namespace$,$pod_name$,$source_external_dynamic_list$,$destination_external_dynamic_list$,$host_id$,$serial_number$,$domain_edl$,$source_dynamic_address_group$,$destination_dynamic_address_group$,$partial_hash$,$high_resolution_timestamp$,$reason$,$justification$,$a_slice_service_type$,$application_subcategory$,$application_category$,$application_technology$,$application_risk$,\"$application_characteristic$\",$application_container$,$tunneled_application$,$application_saas$,$application_sanctioned_state$",
halt: true
},
{
//matches THREAT logs without comma surround list in url_category_list or application_characteristic.
//PAN OS 10.2 will add $cloud_report_id to the end of this log format. If this no longer matches threat logs, check for the extra field at the end.
id: "threat-3",
attributes: { type: "THREAT", format: "threat-3" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$serial_number$,THREAT,$threat_content_type$,\\d+,$Generated_time$,$source_address$,$destination_address$,$nat_source_address$,$nat_destination_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$misc=misc$,$Session_id$,$repeat_count$,$source_port$,$destination_port$,$nat_source_port$,$nat_destination_port$,$flags$,$ip_protocol$,$action$,$url_filename$,$threat_id$,$category$,$severity$,$direction$,$sequence_number$,$action_flags$,$source_location$,$destination_location$,$misc=misc$,$Content_type$,$pcap_id$,$file_digest$,$cloud$,$url_index$,$user_agent$,$file_type$,$x-forwarded-for$,$referer$,$sender$,$subject$,$recipient$,$report_id$,$device_group_hierarchy_level_1$,$device_group_hierarchy_level_2$,$device_group_hierarchy_level_3$,$device_group_hierarchy_level_4$,$virtual_system_name$,$device_name$,$misc=misc$,$Source_vm_uuid$,$destination_vm_uuid$,$http_method$,$tunnel_id_imsi$,$monitor_tag_imei$,$parent_session_id$,$parent_start_time$,$tunnel_type$,$threat_category$,$content_version$,$misc=misc$,$Sctp_association_id$,$payload_protocol_id$,$http_headers$,$url_category_list$,$rule_uuid$,$http_2_connection$,$dynamic_user_group_name$,$xff_address$,$source_device_category$,$source_device_profile$,$source_device_model$,$source_device_vendor$,$source_device_os_family$,$source_device_os_version$,$source_hostname$,$source_mac_address$,$destination_device_category$,$destination_device_profile$,$destination_device_model$,$destination_device_vendor$,$destination_device_os_family$,$destination_device_os_version$,$destination_hostname$,$destination_mac_address$,$container_id$,$pod_namespace$,$pod_name$,$source_external_dynamic_list$,$destination_external_dynamic_list$,$host_id$,$serial_number$,$domain_edl$,$source_dynamic_address_group$,$destination_dynamic_address_group$,$partial_hash$,$high_resolution_timestamp$,$reason$,$justification$,$a_slice_service_type$,$application_subcategory$,$application_category$,$application_technology$,$application_risk$,$application_characteristic$,$application_container$,$tunneled_application$,$application_saas$,$application_sanctioned_state$",
halt: true
},
{
id: "userid",
attributes: { type: "USERID", format: "userid" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$serial_number$,USERID,$threat_content_type$,$misc=misc$,$generated_time$,$virtual_system$,$source_ip$,$user$,$data_source_name$,$event_id$,$repeat_count$,$time_out_threshold$,$source_port$,$destination_port$,$data_source$,$data_source_type$,$sequence_number$,$action_flags$,$device_group_hierarchy_level_1$,$device_group_hierarchy_level_2$,$device_group_hierarchy_level_3$,$device_group_hierarchy_level_4$,$virtual_system_name$,$device_name$,$virtual_system_id$,$factor_type$,$factor_completion_time$,$factor_number$,$user_group_flags$,$user_by_source$,$misc=misc$,$high_resolution_timestamp$",
halt: true
},
{
//dont match on application_characteristic for cases where is it blank.
id: "basic",
attributes: { format: "basic" },
format: "\\<[^\\>]+\\>$timestamp=tsPatternPA$ $hostname$ $log_version$,$receive_time$,$firewall_serial_number$,$type$,$sub_type$,\\d+,$generate_time$,$source_address$,$destination_address$,$source_nat_address$,$destination_nat_address$,$rule_name$,$source_user$,$destination_user$,$application$,$virtual_system$,$source_zone$,$destination_zone$,$inbound_interface$,$outbound_interface$,$log_action$,$time_stamp$,$session_id$,$repeat_count$,$source_port$,$destination_port$,$source_nat_port$,$destination_nat_port$,$flag$,$protocol$,$action$,.*",
},
]
}
}