Check Parent-Child Process
detection_check_parent_childCheck if a parent-child process relationship is expected or suspicious, delivering risk level, MITRE technique, and triage notes.
Instructions
Check if a process parent-child relationship is expected or suspicious.
Provide parent and child process filenames (e.g., parent='winword.exe', child='cmd.exe'). Returns whether the relationship is expected, the risk if unexpected, MITRE technique, and triage notes.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| parent | Yes | ||
| child | Yes | ||
| os_filter | No | windows |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- mcp_server/server.py:151-213 (handler)Main handler function for detection_check_parent_child tool. Takes parent, child, and optional os_filter parameters. Strips paths, loads parent-child baseline data, matches against CSV rows with glob support, and returns found/matches with risk assessment.
def detection_check_parent_child( parent: str, child: str, os_filter: str = "windows", ) -> dict[str, Any]: """Check if a process parent-child relationship is expected or suspicious. Provide parent and child process filenames (e.g., parent='winword.exe', child='cmd.exe'). Returns whether the relationship is expected, the risk if unexpected, MITRE technique, and triage notes. """ parent_lower = parent.lower().strip() child_lower = child.lower().strip() # Strip paths if "\\" in parent_lower or "/" in parent_lower: parent_lower = parent_lower.replace("\\", "/").split("/")[-1] if "\\" in child_lower or "/" in child_lower: child_lower = child_lower.replace("\\", "/").split("/")[-1] matches = [] for row in _get_parent_child(): if row.get("os", "").lower() != os_filter.lower(): continue row_parent = row.get("parent", "").lower() row_child = row.get("child", "").lower() # Check parent match (exact or glob) parent_match = _match_filename(row_parent, parent_lower) # Check child match (exact, glob, or wildcard) child_match = row_child == "*" or _match_filename(row_child, child_lower) if parent_match and child_match: matches.append({ "parent": row.get("parent", ""), "child": row.get("child", ""), "os": row.get("os", ""), "expected": row.get("expected", ""), "risk_if_unexpected": row.get("risk_if_unexpected", ""), "mitre_id": row.get("mitre_id", ""), "context": row.get("context", ""), "notes": row.get("notes", ""), }) if not matches: return { "found": False, "parent": parent_lower, "child": child_lower, "os": os_filter, "assessment": ( "No baseline entry found. This may be suspicious — " "undocumented parent-child relationships warrant investigation." ), "suggestion": ( "Try checking each process individually with " "detection_lookup_binary to see if either is a known LOLBin." ), } # Return most specific match (prefer exact child over wildcard) best = sorted(matches, key=lambda m: (m["child"] == "*", m["expected"] == "true")) return {"found": True, "matches": best} - mcp_server/server.py:142-150 (registration)MCP tool registration decorator with annotations (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) for detection_check_parent_child.
@mcp.tool( annotations={ "title": "Check Parent-Child Process", "readOnlyHint": True, "destructiveHint": False, "idempotentHint": True, "openWorldHint": False, }, ) - mcp_server/server.py:151-160 (schema)Function signature defines the input schema: parent (str), child (str), os_filter (str, default 'windows'). Docstring describes expected usage and return values.
def detection_check_parent_child( parent: str, child: str, os_filter: str = "windows", ) -> dict[str, Any]: """Check if a process parent-child relationship is expected or suspicious. Provide parent and child process filenames (e.g., parent='winword.exe', child='cmd.exe'). Returns whether the relationship is expected, the risk if unexpected, MITRE technique, and triage notes. """ - mcp_server/server.py:43-45 (helper)Helper function _match_filename uses fnmatch glob matching for parent/child process name comparisons.
def _match_filename(pattern: str, value: str) -> bool: """Match with glob support (e.g., 'tomcat*.exe').""" return fnmatch(value.lower(), pattern.lower()) - mcp_server/server.py:61-65 (helper)Helper function _get_parent_child loads parent_child_baselines.csv with caching.
def _get_parent_child() -> list[dict[str, str]]: global _parent_child_cache if _parent_child_cache is None: _parent_child_cache = _load_csv("parent_child_baselines.csv") return _parent_child_cache