**Approach (brief)**Use DFS from each 'external'/'untrusted' source to enumerate all simple paths to any 'sensitive-data' sink. Track visited set to ensure paths are simple (no cycles). Apply pruning: stop exploring when path length exceeds a policy limit, when nodes are known to be safe (whitelisted), or when reaching nodes with no route to sensitive assets (precomputed reachability).**Algorithm (runnable Python)**python
from collections import defaultdict, deque
def find_attack_paths(nodes, edges, node_labels, max_depth=10, whitelist=None):
# nodes: iterable of node ids
# edges: iterable of (u,v)
# node_labels: dict node->label e.g. 'external','untrusted','sensitive-data'
# whitelist: set of nodes considered safe to prune
adj = defaultdict(list)
for u,v in edges:
adj[u].append(v)
# Precompute reverse reachable: nodes that can reach any sensitive node (reverse BFS)
sensitive = {n for n,l in node_labels.items() if l=='sensitive-data'}
rev = defaultdict(list)
for u,v in edges:
rev[v].append(u)
can_reach_sensitive = set()
q = deque(sensitive)
while q:
x = q.popleft()
for p in rev[x]:
if p not in can_reach_sensitive:
can_reach_sensitive.add(p); q.append(p)
starts = [n for n,l in node_labels.items() if l in ('external','untrusted')]
whitelist = set(whitelist or ())
paths = []
def dfs(node, visited, path):
if len(path) > max_depth: return
if node in whitelist: return
if node not in can_reach_sensitive and node not in sensitive: return # prune unreachable
if node in sensitive:
paths.append(path.copy()); return
for nb in adj.get(node,[]):
if nb in visited: continue
visited.add(nb); path.append(nb)
dfs(nb, visited, path)
path.pop(); visited.remove(nb)
for s in starts:
dfs(s, {s}, [s])
return paths
# Example use:
# nodes = [1,2,3,4]; edges=[(1,2),(2,3),(3,4)]; labels={1:'external',4:'sensitive-data'}
**Complexity**Worst-case time is O(S * (b^L)) where S = number of starts, b = average out-degree, L = max path length (or |V|). With simple-path constraint it's exponential in path length; space is O(L) for recursion plus storage of found paths.**Pruning strategies**- Precompute nodes that can reach sensitive assets and skip others.- Whitelist known-safe nodes.- Enforce max_depth to bound exploration.- Optionally memoize dead-ends (nodes that don't lead to any sink) to avoid revisiting.This solution balances exhaustiveness and practical pruning suitable for security architecture threat modeling.