From 57e716b71052a5c2e06bd2cdd121973c6c3f0359 Mon Sep 17 00:00:00 2001 From: oceans2alaska Date: Fri, 30 Oct 2026 12:34:39 -0700 Subject: [PATCH] Replace inventory-counting risk with bounded workload scoring and separate confidence --- README.md | 17 +- docs/DEVELOPMENT.md | 8 +- docs/IMPLEMENTATION_PLAN.md | 4 + docs/SCORING.md | 65 + powershell/Get-ServerStorage.ps1 | 26 +- powershell/README.md | 4 +- samsara/analyzers/assessment.py | 120 +- samsara/analyzers/dependencies.py | 243 +++ samsara/analyzers/policy.py | 55 + samsara/analyzers/summary.py | 13 +- samsara/assets/scoring_policy.json | 18 + samsara/models/inventory.py | 2 +- samsara/reporting/export.py | 99 +- samsara/ui/window.py | 52 +- schemas/inventory-v1.schema.json | 15 + .../fixtures/scoring/active_file_server.json | 1348 ++++++++++++++++ tests/fixtures/scoring/active_iis_server.json | 1357 ++++++++++++++++ tests/fixtures/scoring/active_sql_server.json | 1366 ++++++++++++++++ tests/fixtures/scoring/clean_windows.json | 1328 ++++++++++++++++ tests/fixtures/scoring/domain_controller.json | 1337 ++++++++++++++++ .../scoring/inactive_game_server.json | 1369 +++++++++++++++++ .../scoring/incomplete_collection.json | 1336 ++++++++++++++++ tests/powershell/Test-Collectors.ps1 | 8 +- tests/test_core.py | 3 +- tests/test_scoring.py | 215 +++ tests/test_ui.py | 30 + 26 files changed, 10351 insertions(+), 87 deletions(-) create mode 100644 docs/SCORING.md create mode 100644 samsara/analyzers/dependencies.py create mode 100644 samsara/analyzers/policy.py create mode 100644 samsara/assets/scoring_policy.json create mode 100644 tests/fixtures/scoring/active_file_server.json create mode 100644 tests/fixtures/scoring/active_iis_server.json create mode 100644 tests/fixtures/scoring/active_sql_server.json create mode 100644 tests/fixtures/scoring/clean_windows.json create mode 100644 tests/fixtures/scoring/domain_controller.json create mode 100644 tests/fixtures/scoring/inactive_game_server.json create mode 100644 tests/fixtures/scoring/incomplete_collection.json create mode 100644 tests/test_scoring.py diff --git a/README.md b/README.md index a40e496..b61eb36 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Choose a new destination in the final line to retain earlier evidence. This dire | Networking | TCP listeners/connections, endpoints, PID/name | Snapshot only; direction, historical use, UDP and protected processes may be unknown | | Tasks | Visible task names/paths, state, enabled setting, principal, safe executable basename and selected trigger fields | No arguments, XML, inline scripts, event query details or execution history; visibility depends on account | | Software | Machine uninstall registrations in both registry views | No Win32_Product; per-user, portable, container and unregistered applications not covered | -| Storage / shares | Local volumes, capacity/free space, configured SMB share paths including administrative shares | No contents/ACLs/consumers, backups or external-array relationships | +| Storage / shares | Local volumes, capacity/free space, configured SMB share paths including administrative shares | No contents/ACLs/backups; optional read-only open-file aggregate counts identify business-share activity only when the share root can be correlated without an administrative alias | | Certificates | Subject, issuer, expiry, thumbprint and machine store | No private keys, user stores, file-based certificates or application bindings | Each check includes status, UTC start/end times, sanitized errors, permission limitations and scope limitations. `complete` means the stated query scope succeeded; it does not prove complete retirement evidence. Sensitive checks run non-elevated are conservatively partial. Some Windows providers can silently filter objects by ACL; even elevated results need administrator review. Partial accessible data are retained. Invalid/unsupported Server identity stops the assessment; failed other checks never receive demo replacements. @@ -115,7 +115,18 @@ Collection is read-only, requires no sudo and does not restart services, configu The summary highlights name-matched server software and running application services; this is not a complete application classifier. Scheduled entries show configured work, not confirmed execution, and cron timezones are unverified. Full inventory and collection limitations remain available in the tabs and exported report. -Risk is a preliminary heuristic requiring administrator review. Missing connections do not prove non-use, and the tool never declares a server safe for retirement. Review exported hostnames, paths and addresses before sharing. +## Retirement risk and confidence + +Scoring model v2 scores grouped workload evidence, not inventory counts. The final 0–100 score is the **strongest workload score**; services, ports and clients supporting the same workload are not added. Standard Windows services/features, default administrative shares, browser/Steam client activity, unknown traffic and collection failures do not automatically receive risk points. Installed/stopped software is not active-use evidence. + +Coverage percentage and evidence confidence are displayed separately. Failed task/certificate checks lower confidence and require review without implying an active application. Low/zero risk never grants retirement clearance: historical usage validation, business ownership and administrator approval are always required. + +Configure scoring through **Settings → Load scoring policy JSON**, starting with `samsara/assets/scoring_policy.json`. The applied policy and exact per-workload score explanation are recorded in JSON and HTML; history keeps its original policy. See [scoring rules, limitations and regression scenarios](docs/SCORING.md). + +To re-score the test server without contacting it again, restart Samsara and open its previous JSON inventory or assessment using **Saved inventory file**. Existing v1 raw inventory is compatible; old scores are ignored and recomputed. The saved timestamp/source remain explicit. The original report JSON was not provided for development, so the 1,565-point regression uses synthetic data matching the reported counts. + +HTML now summarizes actionable findings and confidence rather than dumping hundreds of services. Each HTML export creates a linked, content-addressed inventory JSON file beside it. Keep both files together; the JSON assessment export also retains all inventory. Every routine service, feature, administrative share and connection remains available in the detailed UI tabs and full JSON. + ## Headless Linux inventory @@ -133,4 +144,4 @@ Use a new filename; existing files are not overwritten. Open the result using ** python -m unittest discover -s tests -v ``` -Qt tests run offscreen. See [platform support](docs/PLATFORMS.md), [architecture and plan](docs/IMPLEMENTATION_PLAN.md) and [developer guide](docs/DEVELOPMENT.md). Run from source; Windows packaging and real-host collector acceptance remain pending. PowerShell AST/synthetic tests run when a native runtime is available and are explicitly skipped otherwise. On this Linux development host, 47 tests were discovered: 46 passed and the native PowerShell test was skipped; no real Windows inventory has been collected. +Qt tests run offscreen. See [platform support](docs/PLATFORMS.md), [architecture and plan](docs/IMPLEMENTATION_PLAN.md) and [developer guide](docs/DEVELOPMENT.md). Run from source; Windows packaging and real-host collector acceptance remain pending. PowerShell AST/synthetic tests run when a native runtime is available and are explicitly skipped otherwise. On this Linux development host, 61 tests were discovered: 60 passed and the native PowerShell test was skipped; no real Windows inventory has been collected. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 07e716c..e440ea2 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -22,7 +22,7 @@ Report source is included in locally collected server metadata. Import removes t ## Analysis -Each finding has classification, supporting evidence, severity and points. Score is capped at 100: 0 informational, 1–29 low, 30–59 medium, 60–89 high, 90–100 critical. This is demonstration heuristic logic, not a validated production risk model. Known Linux application service names receive potential-dependency findings; other running services remain unknown rather than all being classified as application workloads. Incomplete checks and observation limits always require review. +Model v2 groups workload evidence, selects its strongest rule and takes the maximum workload score. It never sums inventory observations. `dependencies.py` classifies OS/client/unknown activity separately and correlates positive nonzero PIDs with listeners; `policy.py` validates the data-only weights/thresholds; `assessment.py` computes the audit breakdown and independent coverage/confidence. Full policy identity, evidence references and score-determining groups are serialized. Thresholds and limitations are documented in SCORING.md. No result grants retirement approval. ## Verification @@ -49,3 +49,9 @@ OS ProductType 2/3 plus build mapping gates Server 2016/2019/2022/2025. Workstat `models/inventory.py` now owns the shared v1 validator and field allowlists. Metadata fields are additive and old demo/Linux results remain valid. Live Windows results are projected to safe fields; raw CIM instances are never serialized. Imported errors retain recognized categorical codes and timestamps but discard arbitrary diagnostic/permission text. Imports cannot assert the trusted local source label. Registry software covers both read-only machine uninstall views; services omit PathName; tasks emit safe action basenames and selected triggers rather than Arguments/XML; certificates never access private-key properties. Reports include all check statuses, timestamps and limitations. Fixtures for all four Server releases live in tests/fixtures/windows. Python tests cover contract/identity/array handling, partial permission results, invalid output, process cancellation/reaping, timeouts, output bounds, secret-field exclusion, imports and the unchanged GUI/history/report flow. `tests/powershell/Test-Collectors.ps1` uses the native PowerShell AST parser and synthetic function/registry objects to test the scripts without querying the host; it runs automatically where PowerShell is present. This test is skipped in the current Linux environment. **No real Windows inventory has yet been produced here; do not mark the Windows milestone complete.** Use the README's explicit real-host validation command and review private reports before recording compatibility. + +## Model v2 UI/report behavior + +Settings can load validated scoring JSON or restore defaults. The policy is snapshotted at job start; history keeps prior model/policy results. Summary displays evidence coverage and confidence independently of workload risk. Networking augments the table with inferred/reported direction and rationale without changing the raw rows. Dependencies excludes OS/unknown context; Risk Findings retains zero-point review context and the exact maximum-score formula. The HTML report writes a content-addressed full-inventory sidecar and links it rather than embedding raw inventory. Tests cover sidecar collision safety, escaping, preservation and reimport. + +Windows shares now optionally query Get-SmbOpenFile read-only. They match full paths against share root plus ShareRelativePath, avoiding administrative drive access being mistaken for nested business-share use. Only aggregate counts/time/source are exported. Same-root administrative aliases are ambiguous. Native scripts/tests require Windows validation; the Python fixtures distinguish confirmed aggregate activity from generic SMB transport. diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md index 46f8728..fc16f00 100644 --- a/docs/IMPLEMENTATION_PLAN.md +++ b/docs/IMPLEMENTATION_PLAN.md @@ -34,3 +34,7 @@ The original empty repository had no existing application or project instruction ## Windows local collection milestone Added independent read-only PowerShell modules and the local Python backend, without replacing the existing application. OS identity is accepted only for known Server build/product-type combinations. The existing QThread routes Windows auto-detect/profile choices to the backend and retains the same result, history and reporting flow. Python runs each fixed local section in a separate supervised PowerShell process, with cancellation, per-section/overall budgets, output bounds and cleanup. Per-check UTC timestamps, sanitized errors, scope and permission limitations extend schema v1 additively; old fixtures remain valid. The real-host validation tool produces full private evidence and a separate metadata-only summary for review. Remote Windows collection is not part of this milestone. + +## Retirement scoring correction + +Implemented model v2 using the existing analyzer/report/UI boundaries: workload correlation and OS/client/unknown classification, maximum-of-grouped-evidence scoring, independent coverage/confidence, validated configurable policy and full audit metadata. HTML now leads with actionable findings and saves complete inventory separately. Regression scenarios and the synthetic 1,565-point incident reconstruction are documented in SCORING.md. Remote collection remains outside this change. diff --git a/docs/SCORING.md b/docs/SCORING.md new file mode 100644 index 0000000..9943c07 --- /dev/null +++ b/docs/SCORING.md @@ -0,0 +1,65 @@ +# Retirement scoring model v2 + +## Purpose and migration + +The previous engine added points for every running service, installed role, listener, connection and administrative share. A routine Windows snapshot could accumulate thousands of raw points and saturate at Critical. Model v2 removes that inventory-counting behavior. Collection schema remains v1; assessment metadata is versioned separately as `model_version: 2.0`. + +Re-analyze a previous JSON report through **Analyze → Saved inventory file**. Its raw inventory and collection time are retained; the old assessment is ignored and a v2 assessment is calculated with the selected policy. No new Windows access is needed to re-score existing evidence. Imported data remain explicitly unverified. Old HTML scores do not change; generate a new report. Scores from v1 and v2 are not directly comparable. + +## Evidence classifications + +- **Confirmed workload dependency:** observed activity attributed to a recognized application listener, a verified domain-controller identity, or read-only SMB open-file evidence correlated to a business share root. For TCP this confirms observed transport activity, not connection initiation, business ownership, application health, or permission to retire. +- **Potential workload dependency:** a meaningful installed workload role, running recognized application service, attributed listener, non-administrative share or nonroutine configured schedule. Configuration alone does not prove current use. +- **Standard operating system activity:** baseline services/components, administrative shares, management networking and recognized maintenance schedules. Zero workload points; complete data remain in inventory. +- **Unknown / insufficient evidence:** unmatched services, direction/ownership gaps, client activity, installed/stopped historical application hints and failed checks. Unknown observations never imply an active workload and never imply non-use. + +Classification is conservative and name-based in places. Unknown applications, custom service names and interpreters such as Java cannot be reliably mapped from a port alone. SQL client libraries, stopped database/game services, browsers and Steam client traffic do not establish hosted workloads. Review unmatched services with owners; a low score can reflect rule/collection gaps. + +## Bounded scoring + +1. Group correlated observations by workload family (SQL, IIS, web, file sharing, etc.). Multiple instances or clients in a family do not add points. +2. Select the strongest applicable rule within each workload group. A service, its listener and 100 connections remain one workload score. +3. Final score is **the maximum workload score**, or zero if none are scored. Scores are never added, even across independent workload families. Independent workloads still appear separately for administrator review. + +| Strongest evidence | Default workload score | +| --- | --- | +| Meaningful installed workload role only | 15 | +| Running recognized application service | 35 | +| Listener attributed to a recognized application | 45 | +| Application transport activity on an attributed listener | 70 | +| Configured non-administrative share | 20 | +| File-sharing transport with share access still unverified | 40 | +| Business-share-root-correlated open files | 80 | +| Enabled custom/nonroutine schedule | 20 | +| CIM ProductType=2 domain controller identity | 95 | + +Thresholds: 0 informational, 1–24 low, 25–59 medium, 60–89 high, 90–100 critical. These are triage categories, not probabilities or a validated retirement-readiness percentage. A running IIS service without identifiable worker traffic is potential evidence (35), not automatically an actively consumed application. HTTP.sys `System`/PID 4 traffic alone cannot identify an IIS site; those connections remain unknown unless more precise attribution is available. A role install without a domain-controller identity is not a verified DC. + +The bundled policy is `samsara/assets/scoring_policy.json`. Copy it, adjust documented integer weights/ordered thresholds and coverage weights, then use **Settings → Load scoring policy JSON**. Data-only policies accept no executable expressions. Changes apply to future assessments; history retains earlier results and policy snapshots. Every assessment includes the full policy, SHA-256 identity, selected rules, evidence references and which groups determine the final maximum. Co-leading groups can share that maximum; their scores still are not added. Raw evidence references such as `services[4]` and `network[12]` point into the separately preserved inventory arrays. + +## Direction and correlation + +- Listener address/port plus the same nonzero owning PID can support **Inbound (inferred)**. This does not capture the initiating TCP handshake. A known application attribution is also required to score observed application clients. +- Explicit source direction is labeled **reported**, not independently verified, and still requires listener/process correlation to upgrade to observed application-client evidence. +- Ephemeral local ports to non-ephemeral remote ports without an attributed listener can be **Outbound (inferred)**. The port-range assumption is heuristic and may differ on configured hosts. +- Loopback, IPv4-mapped loopback and same-host addresses are identified first. These and outbound/unknown directions do not become external client evidence. +- A bare 443, 1433 or game-related port never identifies an application. Steam and browser process activity never create a dedicated game-server workload. +- Listener matching uses local address compatibility and a positive PID, not just a common port. HTTP.sys, shared processes, missing PIDs, process exits, dual-stack behavior and non-TCP activity can leave attribution unknown. + +File-server confirmation is stricter than TCP 445: a connection could be administrative access. `Get-SmbOpenFile` reads open-file metadata; the collector requires `Path = configured share root + ShareRelativePath` to distinguish access through an administrative drive root from a nested business share. It exports only aggregate counts and observation time. Same-root administrative aliases are explicitly ambiguous and cannot confirm business-share use. Same-root business aliases cannot be individually distinguished and remain one workload family. File contents, file names, session user names and client identities are not exported by this query. Denied access reduces confidence. Zero open files never proves inactivity. See [Microsoft's Get-SmbOpenFile metadata](https://learn.microsoft.com/en-us/powershell/module/smbshare/get-smbopenfile). + +## Coverage, confidence and review + +Coverage is calculated separately from weighted check statuses: complete=1, partial=0.5 by default, failed/not_collected/missing=0. Permission limitations or errors on a nominally complete check conservatively receive partial credit. Required scopes and weights are explicit in policy. Scope limitations continue to apply even with 100% coverage. + +Evidence confidence is low below 60% coverage, moderate below 90% or with incomplete checks/unclassified services/unknown directions, and high otherwise. Confidence describes the collected and classified snapshot, not historical inactivity. It is not a statistical confidence interval. Checks with access errors trigger manual review; they never add workload score. Positive evidence is not erased when unrelated collection is incomplete. + +Historical monitoring over a representative business cycle, infrequent workloads, ownership, service accounts, external dependencies, backups/retention, rollback and administrator approval must be verified before retirement. `review_required` is always true. The application does not declare a server safe to retire. + +## Reporting and validation + +HTML leads with actionable workload groups, scoring rationale, confidence and missing checks. Routine inventory is summarized in zero-point context groups rather than repeated as medium-risk dependencies. A content-addressed `.inventory-.json` sidecar preserves every collected inventory row; its SHA-256 and link are in the report. Keep it with the HTML. JSON assessment export also retains full inventory, model/policy and detailed observations. Existing different sidecars are never overwritten. + +Regression fixtures cover a clean Windows Server, an inactive former game server, active SQL, active IIS, a domain controller, business file-share use and incomplete collection. An additional regression reconstructs the reported 1,565-point pattern (86 services, 19 connections, 14 features, 23 listeners, three admin shares and two failed checks): v2 gives zero workload risk and reduced confidence, while retaining all observations. This is a synthetic reproduction of the supplied counts, not a replay of an unavailable original inventory file. + +These tests do not certify business dependency discovery. Re-score the real test server's saved JSON, review results with its owner, and validate any new SMB activity fields on Windows. Native PowerShell tests remain skipped in the Linux development environment. diff --git a/powershell/Get-ServerStorage.ps1 b/powershell/Get-ServerStorage.ps1 index 7563896..028db74 100644 --- a/powershell/Get-ServerStorage.ps1 +++ b/powershell/Get-ServerStorage.ps1 @@ -19,5 +19,29 @@ function Get-SamsaraShares { capacity_gib = $null; free_gib = $null; special = [bool]$_.Special } }) } - $Result.limitations += 'Configured SMB shares, including administrative shares. No share contents, ACLs, credentials, sessions or consumers queried. Existence does not establish use.' + Invoke-SamsaraRead $Result 'SMB open-file activity' { + $openFiles = @(Get-SmbOpenFile -ErrorAction Stop) + $observedAt = Get-SamsaraTimestamp + foreach ($share in $Result.data) { + if ($share.name -match '^(ADMIN|IPC|[A-Z])\$$' -or -not $share.path) { continue } + $prefix = $share.path.TrimEnd([char]'\') + '\' + $matching = @($openFiles | Where-Object { + # Path alone also matches a file opened through C$/D$. Require + # Path = share root + ShareRelativePath to distinguish that case. + $_.Path -and $_.ShareRelativePath -and + ([string]$_.Path).Equals(($prefix + ([string]$_.ShareRelativePath).TrimStart([char]'\')), [StringComparison]::OrdinalIgnoreCase) + }) + $adminAliases = @($Result.data | Where-Object { + $_.name -match '^(ADMIN|IPC|[A-Z])\$$' -and ([string]$_.path).TrimEnd([char]'\').Equals($share.path.TrimEnd([char]'\'), [StringComparison]::OrdinalIgnoreCase) + }) + # Only aggregated, path-correlated activity is exported. Never serialize + # open-file objects, file names, session identities or user names. + $share | Add-Member -NotePropertyName observed_open_files -NotePropertyValue $matching.Count + $clients = @($matching | Select-Object -ExpandProperty ClientComputerName -Unique) + $share | Add-Member -NotePropertyName observed_client_count -NotePropertyValue $clients.Count + $share | Add-Member -NotePropertyName activity_source -NotePropertyValue $(if ($adminAliases.Count) { 'Get-SmbOpenFile (administrative alias ambiguous)' } else { 'Get-SmbOpenFile (share-root-correlated)' }) + $share | Add-Member -NotePropertyName activity_observed_at -NotePropertyValue $observedAt + } + } + $Result.limitations += 'Configured SMB shares, including administrative shares, and aggregate open-file counts share-root-correlated to business shares. No file contents, open-file paths, session identities, ACLs or credentials are exported. Zero open files does not prove a share unused. Same-root aliases are not individually distinguishable; an administrative same-root alias is marked ambiguous and never confirms business-share use. All business shares remain one workload group.' } diff --git a/powershell/README.md b/powershell/README.md index bcb573f..66db419 100644 --- a/powershell/README.md +++ b/powershell/README.md @@ -12,7 +12,7 @@ Targets: Windows Server 2016/2019/2022/2025, Windows PowerShell 5.1, 64-bit exec | Get-ServerNetwork.ps1 | Get-NetTCPConnection and Get-Process names/IDs | | Get-ServerTasks.ps1 | Get-ScheduledTask visible definitions, accounts, safe executable basenames, selected triggers | | Get-ServerSoftware.ps1 | HKLM uninstall registrations using explicit Registry32/Registry64 read-only handles | -| Get-ServerStorage.ps1 | Get-Volume and Get-SmbShare; volumes/shares share the existing storage inventory category | +| Get-ServerStorage.ps1 | Get-Volume, Get-SmbShare and aggregate Get-SmbOpenFile share-root correlation; volumes/shares share the existing storage inventory category | | Get-ServerCertificates.ps1 | Public metadata in accessible LocalMachine certificate stores | | Inventory.Common.ps1 | Section timing, safe errors and permission limitations | @@ -25,3 +25,5 @@ Direct full-script use has no Python timeout/cancellation supervisor; use the Py See README.md for exact GUI, headless and real-host validation instructions. `tests/powershell/Test-Collectors.ps1` parses scripts and mocks their data sources; this is synthetic testing, not real host validation. Implementation references: [Get-WindowsFeature](https://learn.microsoft.com/en-us/powershell/module/servermanager/get-windowsfeature), [Win32_Service](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-service), [Get-NetTCPConnection](https://learn.microsoft.com/en-us/powershell/module/nettcpip/get-nettcpconnection), [Get-ScheduledTask](https://learn.microsoft.com/en-us/powershell/module/scheduledtasks/get-scheduledtask), [registry software inventory](https://learn.microsoft.com/en-us/powershell/scripting/samples/working-with-software-installations), [certificate provider](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/about/about_certificate_provider). + +Scoring v2 excludes default administrative shares, but keeps them in inventory. Business-share client confirmation requires share-root-correlated open-file counts, not just TCP 445 activity. The read-only activity query exports no file names, session users or client names; same-root administrative aliases remain ambiguous. See docs/SCORING.md. This new query still requires native Windows validation. diff --git a/samsara/analyzers/assessment.py b/samsara/analyzers/assessment.py index 466c214..659c344 100644 --- a/samsara/analyzers/assessment.py +++ b/samsara/analyzers/assessment.py @@ -1,44 +1,78 @@ -def assess(data): - inventory = data["inventory"] +"""Bounded workload risk, distinct from inventory coverage and uncertainty.""" +from collections import Counter +from samsara.analyzers.dependencies import classify_dependencies, CONFIRMED, POTENTIAL, STANDARD, UNKNOWN +from samsara.analyzers.policy import load_policy, policy_id, risk_level + +REVIEW = 'Historical usage validation, application-owner verification and administrator approval are required. No active connections does not prove a server is unused.' + + +def assess(data, policy=None): + policy = load_policy(policy) + groups, aggregates, network, tasks = classify_dependencies(data) findings = [] - def add(category, classification, evidence, severity, points): - findings.append(dict(category=category, classification=classification, evidence=evidence, - severity=severity, points=points)) - for row in inventory.get("roles", []): - add("Installed role", "Potential dependency", row["name"], "medium", 10) - linux_apps = ('nginx', 'apache2', 'httpd', 'mysql', 'mysqld', 'mariadb', 'postgresql', - 'redis', 'docker', 'containerd', 'smbd', 'smb', 'nfs-server', 'named', 'haproxy', 'tomcat') - unclassified = [] - for row in inventory.get("services", []): - if row.get("state") == "Running": - if data['server'].get('platform') == 'linux' and not any( - row['name'].split('@')[0].split('.')[0] == app or row['name'].startswith(app + '-') - for app in linux_apps - ): - unclassified.append(row['name']) - continue - add("Active service", "Potential dependency", f"{row['name']} running as {row.get('account', 'unknown')}", "medium", 10) - if unclassified: - add("Unclassified services", "Unknown or insufficient evidence", - f"{len(unclassified)} running Linux services lack an application rule. Administrator review required; examples: " + ', '.join(unclassified[:8]), "low", 5) - for row in inventory.get("network", []): - if row.get("state") == "Established": - add("Network activity", "Confirmed observed activity", f"{row['local_address']}:{row['local_port']} ↔ {row['remote_address']}:{row['remote_port']}; direction unverified", "high", 20) - elif row.get("state") == "Listen": - add("Listening port", "Potential dependency", f"TCP {row['local_port']} ({row.get('process', 'unknown')})", "medium", 5) - for row in inventory.get("tasks", []): - if row.get("enabled"): - add("Scheduled workload", "Potential dependency", f"{row['name']}; account {row.get('account', 'unknown')}", "medium", 10) - for row in inventory.get("storage", []): - if row.get("type") in ("SMB Share", "NFS Export", "Samba Share"): - add("Shared storage", "Potential dependency", f"{row['name']}: {row['path']}", "high", 15) - for row in inventory.get("storage", []): - if row.get("type") == "Remote Mount": - add("Remote storage consumer", "Potential dependency", f"{row['name']} mounted at {row['path']}; access and use unverified", "medium", 10) - for name, check in data["checks"].items(): - if check["status"] != "complete": - add("Missing evidence", "Unknown or insufficient evidence", f"{name}: {check['message']}", "medium", 10) - add("Observation limits", "Unknown or insufficient evidence", "Point-in-time inventory cannot establish infrequent, offline, historical or external dependencies. No active connections does not prove a server is unused.", "low", 5) - score = min(100, sum(f["points"] for f in findings)) - level = "critical" if score >= 90 else "high" if score >= 60 else "medium" if score >= 30 else "low" if score else "informational" - return {"score": score, "level": level, "findings": findings} + breakdown = [] + for group_id, group in sorted(groups.items()): + rule = max(sorted(group['rules']), key=lambda name: policy['scores'][name]) + score = policy['scores'][rule] + references = sorted(group['evidence']) + selected_refs = [ref for ref in references if group['evidence_rules'][ref] == rule] + visible_refs = (selected_refs[:3] + [ref for ref in references if ref not in selected_refs])[:6] + examples = [group['evidence'][ref] for ref in visible_refs] + if len(references)>6: + examples.append(f'{len(references)-6} additional supporting inventory observations; see evidence_refs in JSON.') + classification = CONFIRMED if group['confirmed'] else POTENTIAL + reason = f"Rule {rule} assigns {score}/100. Strongest rule within workload {group_id}; corroborating services/ports/connections are not added." + findings.append({'id':group_id, 'category':group['title'], 'classification':classification, + 'evidence':'\n'.join(examples), 'evidence_refs':references, + 'severity':risk_level(score,policy), 'points':score, 'scoring_reason':reason}) + breakdown.append({'workload':group_id,'title':group['title'],'classification':classification, + 'selected_rule':rule,'candidate_rules':sorted(group['rules']),'selected_evidence_refs':selected_refs, + 'score':score,'evidence_count':len(references),'reason':reason}) + for (category,classification), observations in sorted(aggregates.items()): + examples = [text for _,text in observations[:3]] + findings.append({'id':'context:'+category,'category':category,'classification':classification, + 'evidence':f'{len(observations)} observations. ' + ' '.join(examples), + 'evidence_refs':[ref for ref,_ in observations], 'severity':'informational','points':0, + 'scoring_reason':'Context/uncertainty only; no evidence-based workload score.'}) + checks = data.get('checks',{}) + incomplete = [] + earned, possible = 0.0, sum(policy['coverage_weights'].values()) + for name,weight in policy['coverage_weights'].items(): + check = checks.get(name,{}) + status = check.get('status','not_collected') + if status == 'complete' and (check.get('errors') or check.get('permission_limitations')): + status = 'partial' + earned += weight * (1 if status=='complete' else policy['partial_check_credit'] if status=='partial' else 0) + for name in sorted(set(checks) | set(policy['coverage_weights'])): + check = checks.get(name, {'status':'not_collected','message':'No status supplied for this assessment scope.'}) + if check['status'] != 'complete' or check.get('permission_limitations') or check.get('errors'): + incomplete.append({'check':name,'status':check['status'],'message':check['message']}) + if incomplete: + findings.append({'id':'coverage','category':'Missing evidence','classification':UNKNOWN, + 'evidence':'\n'.join(f"{row['check']}: {row['status']} — {row['message']}" for row in incomplete), + 'evidence_refs':['checks.'+row['check'] for row in incomplete], + 'severity':'informational','points':0, + 'scoring_reason':'Collection gaps reduce confidence and trigger review; they do not establish an active workload.'}) + findings.append({'id':'observation-limits','category':'Observation limits','classification':UNKNOWN, + 'evidence':REVIEW,'evidence_refs':[], 'severity':'informational','points':0, + 'scoring_reason':'General observation limits never add retirement risk points.'}) + score = max((row['score'] for row in breakdown), default=0) + for row in breakdown: + row['determines_final_score'] = row['score']==score and score>0 + for finding in findings: + finding['determines_final_score'] = finding['points']==score and score>0 + coverage_percent = round(100*earned/possible) + unknown_services = len(aggregates.get(('Unclassified services',UNKNOWN),[])) + unknown_directions = sum(row['direction']=='Unknown' for row in network) + confidence = 'low' if coverage_percent<60 else 'moderate' if coverage_percent<90 or incomplete or unknown_services or unknown_directions else 'high' + return {'model_version':'2.0','score':score,'level':risk_level(score,policy),'findings':findings, + 'scoring':{'formula':'Maximum grouped workload score (0–100); never a sum of inventory observations.', + 'policy_id':policy_id(policy),'policy':policy,'breakdown':breakdown, + 'explanation':'No scored workload evidence was identified. This is not retirement clearance.' if not score else f"Final {score}/100 is the highest workload score; other workloads and supporting observations are not added."}, + 'coverage':{'percent':coverage_percent,'incomplete_checks':incomplete, + 'method':'Weighted complete checks; partial checks receive policy-defined credit. Missing checks receive zero. Scope limitations still apply.'}, + 'confidence':{'level':confidence,'unclassified_services':unknown_services,'unknown_connection_directions':unknown_directions, + 'scope':'Confidence concerns collected/classified evidence only, not historical inactivity or retirement readiness.'}, + 'network_observations':network,'task_observations':tasks, + 'classification_counts':dict(Counter(row['classification'] for row in findings)), + 'review_required':True,'manual_verification':REVIEW} diff --git a/samsara/analyzers/dependencies.py b/samsara/analyzers/dependencies.py new file mode 100644 index 0000000..38ac0a3 --- /dev/null +++ b/samsara/analyzers/dependencies.py @@ -0,0 +1,243 @@ +"""Correlate workload evidence without altering collected inventory. + +A TCP snapshot cannot prove connection initiation. Listener/PID inference is +explicitly labeled; a confirmed finding describes observed application transport +activity, not verified business ownership or permission to retire. +""" +from collections import defaultdict +import ipaddress +import re + +CONFIRMED = 'Confirmed workload dependency' +POTENTIAL = 'Potential workload dependency' +STANDARD = 'Standard operating system activity' +UNKNOWN = 'Unknown / insufficient evidence' + +FAMILIES = { + 'directory': ('Directory services', ('ntds',), (), ()), + 'sql': ('SQL Server', ('mssqlserver','sqlserveragent'), ('mssql$','sqlagent$'), ('sqlservr',)), + 'iis': ('IIS application hosting', ('w3svc',), (), ('w3wp','iisexpress')), + 'web': ('Web application hosting', ('nginx','apache2','httpd','haproxy','tomcat'), ('tomcat',), ('nginx','apache2','httpd','haproxy','tomcat')), + 'postgres': ('PostgreSQL', ('postgresql',), ('postgresql-',), ('postgres','postmaster')), + 'mysql': ('MySQL / MariaDB', ('mysql','mysqld','mariadb'), ('mysql','mariadb'), ('mysqld','mariadbd')), + 'redis': ('Redis', ('redis','redis-server'), ('redis-',), ('redis-server',)), + 'containers': ('Container workloads', ('docker','containerd','podman'), (), ('dockerd','containerd','podman')), + 'dns': ('DNS server', ('dns','named','bind9'), (), ('named',)), + 'dhcp': ('DHCP server', ('dhcpserver','isc-dhcp-server','kea-dhcp4'), (), ('dhcpd','kea-dhcp4')), + 'ca': ('Certificate authority', ('certsvc',), (), ()), + 'file-sharing': ('Business file shares', ('smbd','smb','nfs-server'), (), ('smbd','nfsd')), + 'game-server': ('Dedicated game server', ('minecraft','minecraft-server','valheim','rustserver'), ('minecraft@','valheim-server'), ('srcds','srcds_linux','hlds','valheim_server','rustdedicated','arkascendedserver','shootergameserver','palserver','bedrock_server')), +} +ROLE_FAMILIES = {'web-server':'iis','dns':'dns','dhcp':'dhcp','adcs-cert-authority':'ca', + 'ad-domain-services':'directory','hyper-v':'virtualization','failover-clustering':'clustering', + 'rds-rd-server':'remote-desktop'} +TITLES = {key: value[0] for key, value in FAMILIES.items()} +TITLES.update({'directory':'Directory services','virtualization':'Virtualization','clustering':'Failover clustering', + 'remote-desktop':'Remote desktop workloads','scheduled-workloads':'Custom scheduled workloads'}) +STANDARD_SERVICES = set('''bfe bits brokerinfrastructure cbdhsvc cdpsvc cdpsusersvc clipsvc coremessagingregistrar cryptsvc + dcomlaunch defragsvc deviceassociationservice dhcp diagnosticshub.standardcollector.service diagtrack dnscache dps + dusmsvc eventlog eventsystem fdphost fdrespub fontcache gpsvc hidserv hvhost iphlpsvc keyiso ktmrm lanmanserver + lanmanworkstation lfsvc licensemanager lmhosts lsm mpssvc msdtc netlogon netman netprofm nlasvc nsi pcasvc + plugplay policyagent power profsvc rasman remoteaccess remoteregistry rpcss rpceptmapper samss schedule seclogon + securityhealthservice sens sense sessionenv shellhwdetection spooler sppsvc ssdpSrv stisvc storSvc swprv sysmain + systemeventsbroker termservice themes timebrokersvc trustedinstaller ualsvc uhssvc umrdpservice usosvc vds vmicheartbeat + vmickvpexchange vmicshutdown vmictimesync vmicvss vss w32time w3logsvc was wdiservicehost wdisystemhost wdnisSvc + wecsvc wersvc winDefend winhttpautoproxysvc winmgmt winrm wlidsvc wpnservice wsearch wuauserv + sqlwriter acpid auditd chronyd crond cron dbus firewalld irqbalance multipathd polkit rsyslog ssh sshd tuned udisks2'''.lower().split()) +STANDARD_PROCESS = {'system','idle','svchost','lsass','services','wininit','winlogon','smss','spoolsv','msmpeng','wmiprvse','wsmprovhost','sshd'} +CLIENT_PROCESS = {'steam','steamservice','steamwebhelper','chrome','msedge','firefox','iexplore','brave','opera','curl','wget'} +MANAGEMENT_PORTS = {22,135,137,138,139,445,3389,5357,5985,5986} +STANDARD_TASK_HINTS = ('apt','dpkg','logrotate','man-db','fstrim','updatedb','systemd-','dnf-','yum-','fwupd','packagekit','run-parts','anacron') + + +def normal_name(value): + return str(value or '').replace('\\','/').rsplit('/',1)[-1].lower().removesuffix('.exe').removesuffix('.service') + + +def service_family(value): + name = normal_name(value) + for key, (_, names, prefixes, _) in FAMILIES.items(): + if name in names or any(name.startswith(prefix) for prefix in prefixes): + return key + return None + + +def process_family(value): + name = normal_name(value) + return next((key for key, (_, _, _, names) in FAMILIES.items() if name in names), None) + + +def is_standard_service(name): + original = str(name).lower() + name = normal_name(name) + return original in STANDARD_SERVICES or name in STANDARD_SERVICES or name.startswith(('systemd-','vmic','onesyncsvc_','cbdhsvc_','cdpusersvc_','wpnuserservice_')) + + +def administrative_share(row): + return row.get('type') in ('SMB Share','Samba Share') and bool(re.fullmatch(r'(?:admin|ipc|[a-z])\$', str(row.get('name','')), re.I)) + + +def address(value): + try: + result = ipaddress.ip_address(str(value).strip('[]').split('%')[0]) + return result.ipv4_mapped if isinstance(result, ipaddress.IPv6Address) and result.ipv4_mapped else result + except ValueError: + return None + + +def known_pid(row): + value = row.get('pid') + return value if type(value) is int and value > 0 else None + + +def classify_dependencies(data): + inventory = data['inventory'] + groups = {} + aggregates = defaultdict(list) + network_observations, task_observations = [], [] + def evidence(group, rule, text, ref, confirmed=False): + entry = groups.setdefault(group, {'id':group, 'title':TITLES.get(group,group), 'rules':set(), + 'evidence':{}, 'evidence_rules':{}, 'confirmed':False}) + entry['rules'].add(rule) + entry['evidence'][ref] = text + entry['evidence_rules'][ref] = rule + entry['confirmed'] |= confirmed + def note(category, classification, text, ref): + aggregates[(category,classification)].append((ref,text)) + services = inventory.get('services',[]) + pid_families = defaultdict(set) + running_families = set() + for index, row in enumerate(services): + ref = f'services[{index}]' + family = service_family(row['name']) + running = str(row.get('state','')).lower() == 'running' + if family and running: + running_families.add(family) + if known_pid(row): + pid_families[known_pid(row)].add(family) + evidence(family,'running_application_service',f"Running service {row['name']}; account {row.get('account','not collected')}. Ownership/use requires review.",ref) + elif family: + note('Historical workload hints',UNKNOWN,f"{row['name']} is {row.get('state','unknown')}; no active service evidence.",ref) + elif is_standard_service(row['name']): + note('OS services',STANDARD,f"{row['name']} ({row.get('state','unknown')}) is a baseline service, not hosted workload evidence.",ref) + elif normal_name(row['name']) in CLIENT_PROCESS or 'steam client' in str(row.get('display_name','')).lower(): + note('Client activity',UNKNOWN,f"{row['name']} is a client component, not evidence of a dedicated server.",ref) + else: + note('Unclassified services',UNKNOWN,f"{row['name']}: no workload rule; review service ownership.",ref) + for index, row in enumerate(inventory.get('roles',[])): + name = row['name'].lower() + ref = f'roles[{index}]' + family = ROLE_FAMILIES.get(name) + if family and str(row.get('state','Installed')).lower() in ('installed','enabled'): + evidence(family,'installed_workload_role',f"Installed role {row['name']}; role installation alone does not prove configured or active workloads.",ref) + else: + standard = name.startswith(('net-framework','powershell','windows-defender','defender','storage-services','file-services','fs-fileserver','wow64','telnet-client','rsat','windows-search')) + note('Installed components',STANDARD if standard else UNKNOWN,f"{row['name']}: installation is not application-use evidence.",ref) + if data['server'].get('platform') == 'windows' and data['server'].get('product_type') == 2: + evidence('directory','verified_domain_controller','CIM ProductType=2 identifies this host as a domain controller. Validate AD replication, FSMO, DNS, authentication and retirement procedures.','server.product_type',True) + business_shares = [] + for index,row in enumerate(inventory.get('storage',[])): + ref = f'storage[{index}]' + if administrative_share(row): + note('Administrative shares',STANDARD,f"{row['name']} is a default administrative share; retained in inventory with no workload score.",ref) + elif row.get('type') in ('SMB Share','Samba Share','NFS Export'): + business_shares.append(row) + evidence('file-sharing','configured_business_share',f"Configured non-administrative share {row['name']}: {row['path']}; consumers/use not established.",ref) + if row.get('activity_source') == 'Get-SmbOpenFile (share-root-correlated)' and type(row.get('observed_open_files')) is int and row['observed_open_files'] > 0: + evidence('file-sharing','observed_business_share_clients',f"{row['name']}: {row['observed_open_files']} open files share-root-correlated by Get-SmbOpenFile at {row.get('activity_observed_at','an unspecified time')}. Current SMB use observed; owner approval still required.",ref+':client_activity',True) + elif row.get('type') == 'Remote Mount': + note('Remote storage consumers',UNKNOWN,f"{row['name']} mounted at {row['path']}; outbound storage use is not evidence of inbound consumers.",ref) + for index,row in enumerate(inventory.get('software',[])): + name = row['name'] + if any(term in name.lower() for term in ('server','sql','nginx','apache','minecraft','valheim','rust','steam')): + note('Installed software hints',UNKNOWN,f"Installed {name}; installation alone does not prove an active hosted workload.",f'software[{index}]') + for index,row in enumerate(inventory.get('tasks',[])): + ref = f'tasks[{index}]' + path,name = str(row.get('path','')).lower(), str(row['name']).lower() + enabled = bool(row.get('enabled')) or str(row.get('state','')).lower() == 'running' + family = service_family(name) or process_family(row.get('executables',row.get('executable',''))) + task_name = normal_name(name.split(' (',1)[0]) + maintenance = any(task_name == hint.rstrip('-') or task_name.startswith((hint.rstrip('-')+'-',hint.rstrip('-')+'.')) for hint in STANDARD_TASK_HINTS) + windows_path = path.replace('/','\\') + os_namespace = windows_path.startswith('\\microsoft\\windows\\') and not windows_path.startswith('\\microsoft\\windows\\powershell\\scheduledjobs') + standard = not family and (os_namespace or maintenance) + classification = STANDARD if standard else POTENTIAL if enabled else UNKNOWN + if enabled and not standard: + group = family or 'scheduled-workloads' + evidence(group,'configured_custom_schedule',f"Enabled task {row['name']} at {row.get('path','unknown path')}; schedule: {row.get('triggers','not collected')}. Configuration does not confirm execution.",ref) + else: + note('OS scheduled maintenance' if standard else 'Disabled schedules',classification,f"{row['name']}: routine maintenance or disabled schedule; no workload points.",ref) + task_observations.append({'inventory_index':index,'classification':classification,'enabled':enabled}) + rows = inventory.get('network',[]) + listener_index = {} + own_addresses = {address(value) for value in data['server'].get('ip_addresses',[])} - {None} + for index,row in enumerate(rows): + local = address(row.get('local_address')) + if local and not local.is_unspecified: + own_addresses.add(local) + if str(row.get('state','')).lower() in ('listen','listening'): + if local is not None and known_pid(row): + listener_index.setdefault((row.get('local_port'),known_pid(row),local),index) + def row_family(row): + family = process_family(row.get('process','')) + choices = pid_families.get(known_pid(row),set()) + if family: + return family + return next(iter(choices)) if len(choices)==1 else None + for index,row in enumerate(rows): + ref = f'network[{index}]' + state = str(row.get('state','')).lower() + proc = normal_name(row.get('process','')) + remote,local = address(row.get('remote_address')), address(row.get('local_address')) + matching = [] + if state in ('established','estab') and local is not None and known_pid(row): + wildcard = ipaddress.ip_address('0.0.0.0' if local.version==4 else '::') + for endpoint in {local,wildcard}: + match = listener_index.get((row.get('local_port'),known_pid(row),endpoint)) + if match is not None: + matching.append(match) + matching.sort() + direction, basis = 'Unknown', 'No reliable direction evidence; a TCP snapshot does not record the initiating handshake.' + if state in ('listen','listening'): + direction,basis = 'Listener','Local listening endpoint; not proof of a consumer.' + elif remote and (remote.is_loopback or remote in own_addresses): + direction,basis = 'Loopback / same host','Remote address is loopback or belongs to this host; no external client inferred.' + elif str(row.get('direction','')).lower() in ('inbound','outbound'): + direction = str(row['direction']).title() + ' (reported)' + basis = 'Direction supplied by inventory source; not independently verified.' + elif state in ('established','estab') and matching: + direction,basis = 'Inbound (inferred)','Local address/port and nonzero PID match a listener. Client initiation is inferred, not observed.' + elif state in ('established','estab') and remote and type(row.get('local_port')) is int and row['local_port']>=49152 and 0 < row.get('remote_port',0)<49152: + direction,basis = 'Outbound (inferred)','Ephemeral local port to a non-ephemeral remote port without an attributed listener; heuristic only.' + family = row_family(row) + classification, rationale = UNKNOWN, 'No attributed application workload. No score from the connection alone.' + if proc in CLIENT_PROCESS: + family = None + rationale = 'Known client process (including browsers/Steam); not a dedicated server workload.' + note('Client activity',UNKNOWN,rationale,ref) + elif family: + if direction == 'Listener': + evidence(family,'attributed_application_listener',f"{row.get('process','unknown')} PID {row.get('pid','unknown')} listening at {row.get('local_address')}:{row.get('local_port')}; a listener alone is potential exposure.",ref) + classification,rationale = POTENTIAL,'Listener attributed to a known application; grouped with service evidence.' + elif state in ('established','estab') and direction.startswith('Inbound') and matching and remote and not remote.is_unspecified: + if family == 'file-sharing': + evidence(family,'observed_smb_transport',f"File-sharing transport observed on local port {row.get('local_port')}; specific share/export use is unverified without correlated open files.",ref) + classification,rationale = POTENTIAL,'File-sharing transport observed; no named business share attributed.' + else: + evidence(family,'observed_application_clients',f"Observed application TCP activity {row.get('local_address')}:{row.get('local_port')} ↔ {row.get('remote_address')}:{row.get('remote_port')}, PID {row.get('pid')}; matches listener(s) {matching}. {basis} Business ownership remains unverified.",ref,True) + classification,rationale = CONFIRMED,'Observed transport activity on an attributed application listener; direction inference is recorded separately.' + else: + note('Other application traffic',UNKNOWN,f"{direction}: {row.get('process','unknown')}; not scored as an inbound consumer. {basis}",ref) + elif business_shares and row.get('local_port') == 445 and (direction == 'Listener' or direction.startswith('Inbound')) and proc in ('system','smbd','unknown','unavailable or process exited'): + family='file-sharing' + if state in ('established','estab') and matching: + evidence(family,'observed_smb_transport',f"SMB TCP activity observed on local port 445 with matching listener PID {row.get('pid')}; the accessed share is unknown and could be administrative. Do not equate this with business-share use.",ref) + classification,rationale = POTENTIAL,'SMB transport plus configured business shares; accessed share unverified without open-file evidence.' + elif proc in STANDARD_PROCESS and (row.get('local_port') in MANAGEMENT_PORTS or (type(row.get('local_port')) is int and row['local_port']>=49152)): + classification,rationale=STANDARD,'Baseline OS/management endpoint; no application workload attributed.' + note('OS networking',STANDARD,rationale,ref) + else: + note('Unattributed networking',UNKNOWN,f"{direction}: {row.get('local_address')}:{row.get('local_port')} ↔ {row.get('remote_address')}:{row.get('remote_port')}. {basis}",ref) + network_observations.append({'inventory_index':index,'direction':direction,'direction_basis':basis, + 'classification':classification,'workload':family or '', 'rationale':rationale}) + return groups, aggregates, network_observations, task_observations diff --git a/samsara/analyzers/policy.py b/samsara/analyzers/policy.py new file mode 100644 index 0000000..7ec96e7 --- /dev/null +++ b/samsara/analyzers/policy.py @@ -0,0 +1,55 @@ +"""Data-only scoring policy. No executable expressions or arbitrary regex rules.""" +import copy +import hashlib +import json +from pathlib import Path + +DEFAULT_POLICY = Path(__file__).resolve().parents[1] / 'assets' / 'scoring_policy.json' +RULES = {'installed_workload_role', 'running_application_service', 'attributed_application_listener', + 'observed_application_clients', 'configured_business_share', 'observed_smb_transport', + 'observed_business_share_clients', 'configured_custom_schedule', 'verified_domain_controller'} + + +def load_policy(source=None): + if source is not None and not isinstance(source,(dict,str,Path)): + raise ValueError('A scoring policy must be a JSON object or file path.') + if isinstance(source, dict): + policy = copy.deepcopy(source) + else: + path = Path(source) if source is not None else DEFAULT_POLICY + try: + if path.stat().st_size > 65536: + raise ValueError('Scoring policy exceeds 64 KiB.') + policy = json.loads(path.read_text(encoding='utf-8')) + except (OSError, UnicodeError, json.JSONDecodeError, RecursionError): + raise ValueError('Unable to read a valid UTF-8 scoring policy JSON file.') from None + try: + limits, weights = policy['thresholds'], policy['coverage_weights'] + valid = ( + set(policy) == {'model_version','name','scores','thresholds','coverage_weights','partial_check_credit'} + and policy['model_version'] == '2.0' and isinstance(policy['name'],str) and 0= policy['thresholds'][name]: + return name + return 'informational' diff --git a/samsara/analyzers/summary.py b/samsara/analyzers/summary.py index 692cadc..8350375 100644 --- a/samsara/analyzers/summary.py +++ b/samsara/analyzers/summary.py @@ -1,8 +1,9 @@ """Compact, evidence-based presentation of the full assessment.""" import re +from samsara.analyzers.dependencies import service_family # Name hints for server workloads, not a comprehensive application classifier. -WORKLOAD = re.compile(r'(?:nginx|apache|httpd|iis|w3svc|sql|postgres|mariadb|mysql|redis|mongo|oracle|tomcat|haproxy|docker|containerd|podman|samba|smbd|nfs|bind|named|rabbitmq|kafka|jenkins|backup|reporting)', re.I) +WORKLOAD = re.compile(r'(?:nginx|apache|httpd|iis|w3svc|sql|postgres|mariadb|mysql|redis|mongo|oracle|tomcat|haproxy|docker|containerd|podman|samba|smbd|nfs|bind|named|rabbitmq|kafka|jenkins|backup|reporting|dedicated server|minecraft|valheim|rustdedicated)', re.I) def summarize(data, assessment): @@ -20,15 +21,17 @@ def summarize(data, assessment): 'evidence': 'Installed software name matches a server-workload hint; use unverified'}) names = {row['name'].lower().split(':')[0] for row in important} for row in inventory.get('services', []): - if row.get('state') == 'Running' and WORKLOAD.search(row['name']) and row['name'].lower().removesuffix('.service') not in names: + if row.get('state') == 'Running' and service_family(row['name']) is not None and row['name'].lower().removesuffix('.service') not in names: important.append({'name': row['name'], 'version': 'Not collected', 'evidence': 'Running service; application ownership requires review'}) jobs = [] - for row in inventory.get('tasks', []): - if row.get('enabled') or row.get('state') == 'Running': + task_classes = {row['inventory_index']: row['classification'] for row in assessment.get('task_observations', [])} + for index, row in enumerate(inventory.get('tasks', [])): + if (row.get('enabled') or row.get('state') == 'Running') and task_classes.get(index) != 'Standard operating system activity': jobs.append({'name': row['name'], 'schedule': row.get('triggers', 'Schedule not collected'), 'status': row.get('state', 'Enabled schedule; execution unverified'), 'account': row.get('account', 'Not collected')}) return {'os_type': os_name, 'version': version, 'risk_score': assessment['score'], 'risk_level': assessment['level'], 'software': important, 'jobs': jobs, - 'incomplete': sum(check['status'] != 'complete' for check in data['checks'].values())} + 'incomplete': len(assessment['coverage']['incomplete_checks']), + 'coverage_percent': assessment['coverage']['percent'], 'confidence': assessment['confidence']['level']} diff --git a/samsara/assets/scoring_policy.json b/samsara/assets/scoring_policy.json new file mode 100644 index 0000000..9521764 --- /dev/null +++ b/samsara/assets/scoring_policy.json @@ -0,0 +1,18 @@ +{ + "model_version": "2.0", + "name": "Conservative retirement workload evidence", + "scores": { + "installed_workload_role": 15, + "running_application_service": 35, + "attributed_application_listener": 45, + "observed_application_clients": 70, + "configured_business_share": 20, + "observed_smb_transport": 40, + "observed_business_share_clients": 80, + "configured_custom_schedule": 20, + "verified_domain_controller": 95 + }, + "thresholds": {"low": 1, "medium": 25, "high": 60, "critical": 90}, + "coverage_weights": {"system": 2, "roles": 1, "services": 2, "network": 2, "tasks": 2, "software": 1, "storage": 1, "shares": 1, "certificates": 1}, + "partial_check_credit": 0.5 +} diff --git a/samsara/models/inventory.py b/samsara/models/inventory.py index eb9b327..6202660 100644 --- a/samsara/models/inventory.py +++ b/samsara/models/inventory.py @@ -25,7 +25,7 @@ ALLOWED = { 'network': {'state', 'local_address', 'local_port', 'remote_address', 'remote_port', 'pid', 'process', 'direction'}, 'tasks': {'name', 'path', 'enabled', 'account', 'triggers', 'state', 'associated_unit', 'executables'}, 'software': {'name', 'publisher', 'version', 'installation_date', 'registry_view'}, - 'storage': {'type', 'name', 'path', 'capacity_gib', 'free_gib', 'filesystem', 'special'}, + 'storage': {'type', 'name', 'path', 'capacity_gib', 'free_gib', 'filesystem', 'special', 'observed_open_files', 'observed_client_count', 'activity_source', 'activity_observed_at'}, 'certificates': {'subject', 'issuer', 'expires', 'thumbprint', 'store'}, } SERVER_FIELDS = {'hostname', 'platform', 'operating_system', 'os_version', 'os_build', 'kernel_version', 'product_type', 'domain_joined', 'last_boot', 'ip_addresses', 'network_adapters', 'domain'} diff --git a/samsara/reporting/export.py b/samsara/reporting/export.py index 4053526..19f70d9 100644 --- a/samsara/reporting/export.py +++ b/samsara/reporting/export.py @@ -1,33 +1,86 @@ +"""Actionable assessment report, with full inventory preserved as a separate JSON artifact.""" +import hashlib import html import json from pathlib import Path +from urllib.parse import quote from samsara.analyzers.summary import summarize -MANUAL_STEPS = ["Confirm application and business owners and obtain retirement approval.", - "Review historical monitoring, DNS, load balancers, backups and firewall logs.", - "Validate service accounts, scheduled workloads and SMB/NFS/Samba consumers with owners.", - "Resolve failed or missing checks and verify rollback and retention requirements."] +MANUAL_STEPS = ['Confirm application and business owners and obtain administrator retirement approval.', + 'Validate historical usage across a representative business cycle, including infrequent and offline jobs.', + 'Review DNS, load balancers, monitoring, backups, firewall logs and service-account consumers.', + 'Verify custom tasks and share consumers; resolve incomplete checks and confirm rollback/retention requirements.'] + def export_json(path, data, assessment): - Path(path).write_text(json.dumps({"inventory_result": data, "assessment": assessment}, indent=2), encoding="utf-8") + Path(path).write_text(json.dumps({'inventory_result':data,'assessment':assessment},indent=2),encoding='utf-8') + + +def inventory_artifact(path, data): + payload = (json.dumps(data,indent=2,ensure_ascii=True)+'\n').encode('utf-8') + digest = hashlib.sha256(payload).hexdigest() + destination = Path(path).with_name(Path(path).stem + '.inventory-' + digest[:12] + '.json') + try: + with destination.open('xb') as output: + output.write(payload) + except FileExistsError: + if destination.read_bytes() != payload: + raise FileExistsError('Existing inventory artifact differs; choose a new report filename.') from None + return destination, digest + + +def table(headers, rows): + escape = lambda value: html.escape(str(value)) + if not rows: + return '

None identified in collected evidence. Review missing evidence before drawing conclusions.

' + return '
' + ''.join('' for h in headers) + '' + ''.join(''+''.join('' for v in row)+'' for row in rows) + '
'+escape(h)+'
'+escape(v)+'
' + def export_html(path, data, assessment): escape = lambda value: html.escape(str(value)) - content = "

Samsara assessment

SYNTHETIC DEMO — no real collection performed.

" if data["synthetic"] else "

Samsara assessment

IMPORTED INVENTORY — source assertions not independently verified.

" - if not data['synthetic'] and data['server'].get('collection_source') == 'Local Linux read-only collection': - content = '

Samsara assessment

LOCAL LINUX INVENTORY — read-only snapshot of the execution host.

' - if not data['synthetic'] and data['server'].get('collection_source') == 'Local Windows read-only collection': - content = '

Samsara assessment

LOCAL WINDOWS INVENTORY — read-only snapshot of the execution host.

' - content += "

Heuristic requiring administrator review. This report does not authorize retirement.

" - summary = summarize(data, assessment) - content += f"

Summary

Retirement risk estimate: {escape(summary['risk_level'])} ({summary['risk_score']}/100). Higher scores mean more retirement concerns.

" - content += f"

OS type: {escape(summary['os_type'])}
Version: {escape(summary['version'])}
Incomplete or missing checks: {summary['incomplete']}

" - content += '

Important software

Workload name hints and application services; use unverified.

    ' - content += ''.join(f"
  • {escape(row['name'])} — {escape(row['version'])}
  • " for row in summary['software']) or '
  • No workload name matches observed; review full inventory and missing checks.
  • ' - content += '

Scheduled jobs and scripts

Configured schedules do not confirm execution.

    ' - content += ''.join(f"
  • {escape(row['name'])} — {escape(row['schedule'])}
  • " for row in summary['jobs']) or '
  • No enabled schedules observed; scheduler coverage may be incomplete.
  • ' - content += '
' - for title, value in [("Server overview", data["server"]), ("Collection timestamp", data["collected_at"]), ("Inventory", data["inventory"]), ("Dependencies and risk findings", assessment["findings"]), ("Collection checks / missing evidence", data["checks"])]: - content += f"

{title}

{escape(json.dumps(value, indent=2))}
" - content += "

Recommended manual verification

    " + "".join(f"
  • {escape(s)}
  • " for s in MANUAL_STEPS) + "
" - Path(path).write_text('Samsara' + content + '', encoding="utf-8") + artifact, digest = inventory_artifact(path,data) + source = 'SYNTHETIC DEMO — no real collection performed.' if data['synthetic'] else 'IMPORTED INVENTORY — source assertions not independently verified.' + for platform in ('Linux','Windows'): + if not data['synthetic'] and data['server'].get('collection_source') == f'Local {platform} read-only collection': + source = f'LOCAL {platform.upper()} INVENTORY — read-only snapshot of the execution host.' + summary = summarize(data,assessment) + scoring = assessment['scoring'] + content = '

Samsara assessment

'+escape(source)+'

' + content += '

Summary

' + content += '

'+escape(data['server']['hostname'])+' · '+escape(data['collected_at'])+'

' + content += f"
Retirement risk estimate: {escape(assessment['level']).title()} · {assessment['score']}/100

{escape(scoring['explanation'])}

" + content += f"

Evidence coverage: {summary['coverage_percent']}% · Evidence confidence: {escape(summary['confidence'])} · {summary['incomplete']} checks need review.

" + content += '

Coverage and uncertainty are separate from risk. A zero or low score is not retirement clearance. Historical usage validation and administrator approval are always required.

' + content += f"

OS type: {escape(summary['os_type'])}
Version: {escape(summary['version'])}

" + actionable = [row for row in assessment['findings'] if row['points']>0] + content += '

Actionable workload findings

' + content += table(['Workload','Classification','Workload score','Evidence','Why this affects scoring'],[(row['category'],row['classification'],row['points'],row['evidence'],row['scoring_reason']) for row in actionable]) + content += '

How the score was calculated

'+escape(scoring['formula'])+'

' + content += table(['Workload','Selected rule','Score','Determines final score','Evidence observations'],[(row['title'],row['selected_rule'],row['score'],'Yes (co-leading)' if row['determines_final_score'] else 'No; below the maximum',row['evidence_count']) for row in scoring['breakdown']]) + content += '
Policy weights, thresholds and audit identity

Policy SHA-256: '+escape(scoring['policy_id'])+'

'+escape(json.dumps(scoring['policy'],indent=2))+'
' + content += '

Important software

Inventory name hints only; installed software does not establish active use.

' + content += table(['Software','Version','Evidence'],[(r['name'],r['version'],r['evidence']) for r in summary['software'][:12]]) + content += '

Scheduled jobs and scripts

Nonroutine enabled schedules; configuration does not confirm execution. All tasks remain in the inventory.

' + content += table(['Job','Schedule','State'],[(r['name'],r['schedule'],r['status']) for r in summary['jobs'][:12]]) + context = [r for r in assessment['findings'] if r['points']==0 and r['id'] not in ('coverage','observation-limits')] + content += '

Unscored context and review items

Routine services, administrative shares, client traffic and unknown observations do not receive workload points.

' + content += table(['Category','Classification','Context'],[(r['category'],r['classification'],r['evidence']) for r in context]) + directions = {} + for row in assessment['network_observations']: + directions[row['direction']] = directions.get(row['direction'],0)+1 + content += '

Network interpretation

Direction inferred from listener/PID or ephemeral-port patterns is a heuristic, not a captured initiating handshake. Confirmed workload findings describe attributed observed transport activity, not verified business ownership.

' + content += table(['Direction / activity','Observations'],list(sorted(directions.items()))) + content += '

Collection checks / missing evidence

Failed and partial checks reduce confidence; they never add workload risk points.

' + checks = [] + for name in sorted(set(data['checks']) | set(assessment['scoring']['policy']['coverage_weights'])): + row = data['checks'].get(name,{'status':'not_collected','message':'No status supplied.'}) + errors = '; '.join(f"{e['code']}: {e['message']}" for e in row.get('errors',[])) + checks.append((name,row['status'],row.get('started_at','Not supplied'),row.get('completed_at','Not supplied'),row['message'],errors,'; '.join(row.get('permission_limitations',[])),'; '.join(row.get('limitations',[])))) + content += table(['Check','Status','started_at (UTC)','completed_at (UTC)','Result','Errors','permission_limitations','Scope limitations'],checks) + content += '

Recommended manual verification

    '+''.join('
  • '+escape(step)+'
  • ' for step in MANUAL_STEPS)+'
' + content += '

Complete raw inventory

Every collected inventory row is preserved separately, including routine services and administrative shares. The analyzer has not changed the collected evidence.

' + content += '

Download / open complete inventory JSON

Inventory SHA-256: '+digest+'

' + content += table(['Inventory section','Rows'],[(name,len(rows)) for name,rows in data['inventory'].items()]) + content += '

JSON assessment export also preserves all raw inventory, classifications, evidence references and policy. Keep the inventory file alongside this HTML when sharing or archiving.

' + css = 'body{font:15px system-ui;max-width:1180px;margin:32px auto;padding:20px;color:#233044;line-height:1.55}h1,h2{color:#173c60}.muted{color:#667}.source,.card,details{background:#eef3f8;padding:16px;border-radius:7px}.table{overflow-x:auto}table{border-collapse:collapse;width:100%;margin:12px 0}th,td{border:1px solid #d9e0e8;text-align:left;vertical-align:top;padding:9px;white-space:pre-wrap;overflow-wrap:anywhere}th{background:#eef3f8}pre{white-space:pre-wrap;overflow-wrap:anywhere}a{color:#1263a3}' + Path(path).write_text('Samsara'+content+'',encoding='utf-8') diff --git a/samsara/ui/window.py b/samsara/ui/window.py index 3845f03..29d8193 100644 --- a/samsara/ui/window.py +++ b/samsara/ui/window.py @@ -12,6 +12,7 @@ from samsara.collectors.windows import collect_local_windows from samsara.core.detection import detect_local_os, check_profile, PROFILES from samsara.analyzers.assessment import assess from samsara.analyzers.summary import summarize +from samsara.analyzers.policy import load_policy, policy_id from samsara.reporting.export import export_html, export_json @@ -101,6 +102,8 @@ class MainWindow(QMainWindow): self.assessment = None self.history = [] self.records = [] + self.scoring_policy = load_policy() + self.active_policy = self.scoring_policy self.setStyleSheet(''' QWidget { background:#131b27; color:#e5ecf5; font-size:14px; } QLabel#title { font-size:27px; font-weight:600; } @@ -254,7 +257,7 @@ class MainWindow(QMainWindow): self.retirement_score = self.label('Retirement risk estimate: —') self.retirement_score.setObjectName('summary') score_layout.addWidget(self.retirement_score) - score_layout.addWidget(self.label('Higher scores mean more retirement concerns. Heuristic only; administrator review is required.', muted=True)) + score_layout.addWidget(self.label('Higher scores mean more workload concerns. Historical usage validation and administrator approval are always required.', muted=True)) self.coverage_note = self.label('', muted=True) score_layout.addWidget(self.coverage_note) summary_layout.addWidget(score_card) @@ -312,8 +315,32 @@ class MainWindow(QMainWindow): settings.addWidget(self.label('Windows Server 2016 / 2019 / 2022 / 2025\nRed Hat Enterprise Linux 7 (legacy) / 8 / 9 / 10 and compatible distributions')) settings.addWidget(self.label('Local Windows Server and Linux collectors are available. Windows validation on a supported host is pending. Remote collection is a separate follow-up. Saved inventories and samples work on either desktop.', muted=True)) settings.addWidget(self.label('Read-only collection • No credentials stored • Session history only', muted=True)) + settings.addWidget(self.label('Retirement scoring policy')) + self.policy_label = self.label('Default policy · ' + policy_id(self.scoring_policy)[:12], muted=True) + settings.addWidget(self.policy_label) + self.policy_button = QPushButton('Load scoring policy JSON…') + self.policy_button.clicked.connect(self.choose_policy) + settings.addWidget(self.policy_button) + self.reset_policy_button = QPushButton('Restore default scoring policy') + self.reset_policy_button.clicked.connect(self.reset_policy) + settings.addWidget(self.reset_policy_button) + settings.addWidget(self.label('Changes apply to new assessments. History keeps the original score and policy. Scores use the strongest workload, never an inventory count.', muted=True)) settings.addStretch() + def choose_policy(self): + path, _ = QFileDialog.getOpenFileName(self, 'Load scoring policy', '', 'Scoring policy (*.json)') + if not path: + return + try: + self.scoring_policy = load_policy(path) + self.policy_label.setText(self.scoring_policy['name'] + ' · ' + policy_id(self.scoring_policy)[:12]) + except ValueError as exc: + QMessageBox.warning(self, 'Invalid scoring policy', str(exc)) + + def reset_policy(self): + self.scoring_policy = load_policy() + self.policy_label.setText('Default policy · ' + policy_id(self.scoring_policy)[:12]) + def source_changed(self): source = self.source.currentData() self.file_row.setVisible(source == 'file') @@ -338,6 +365,7 @@ class MainWindow(QMainWindow): if source == 'file' and not self.inventory_path.text().strip(): self.status.setText('Choose an inventory file before analyzing.') return + self.active_policy = load_policy(self.scoring_policy) self.set_busy(True) self.status.setText('Starting assessment…') self.job = CollectionJob(source, self.target.currentData(), self.inventory_path.text().strip(), self) @@ -348,6 +376,8 @@ class MainWindow(QMainWindow): self.job.start() def set_busy(self, busy): + self.policy_button.setEnabled(not busy) + self.reset_policy_button.setEnabled(not busy) self.analyze_button.setEnabled(not busy) self.source.setEnabled(not busy) self.target.setEnabled(not busy and self.source.currentData() == 'local') @@ -369,7 +399,7 @@ class MainWindow(QMainWindow): return data['server'].get('collection_source', 'Imported inventory — source not independently verified') def collection_done(self, data): - assessment = assess(data) + assessment = assess(data, policy=self.active_policy) self.history.append({'hostname': data['server']['hostname'], 'risk': assessment['level'], 'source': self.source_label(data), 'collected_at': data['collected_at']}) self.records.append((data, assessment)) @@ -377,7 +407,7 @@ class MainWindow(QMainWindow): self.history_empty.hide() self.display_assessment(data, assessment) self.nav.setCurrentRow(0) - incomplete = sum(check['status'] != 'complete' for check in data['checks'].values()) + incomplete = len(assessment['coverage']['incomplete_checks']) self.status.setText(f'Assessment report ready. {incomplete} checks need review. Open the Summary and detail tabs to review the report.') def display_assessment(self, data, assessment): @@ -386,7 +416,7 @@ class MainWindow(QMainWindow): self.result_title.setText(data['server']['hostname']) self.banner.setText(self.source_label(data) + ' • ' + data['collected_at']) self.retirement_score.setText(f"Retirement risk estimate: {summary['risk_score']}/100 · {summary['risk_level'].title()}") - self.coverage_note.setText(f"{summary['incomplete']} checks incomplete or missing. This estimate does not establish retirement readiness.") + self.coverage_note.setText(f"Evidence coverage: {summary['coverage_percent']}% · Confidence: {summary['confidence'].title()} · {summary['incomplete']} checks need review. Coverage gaps do not add risk points.") self.os_type.setText('OS type: ' + summary['os_type']) self.os_version.setText('Version: ' + summary['version']) software = summary['software'] @@ -398,7 +428,7 @@ class MainWindow(QMainWindow): self.scheduled_jobs.setText('\n'.join(f"• {row['name']} — {row['schedule']}" for row in jobs[:5]) + (f"\n+ {len(jobs) - 5} more; see Tasks." if len(jobs) > 5 else '') + '\nSchedules indicate configured work; execution is not confirmed.' if jobs else - 'No enabled schedules observed in collected evidence. Check Tasks and Collection checks for missing scheduler coverage.') + 'No nonroutine enabled schedules identified. Full schedules and missing scheduler coverage are in Tasks and Collection checks.') self.quick_save.setEnabled(True) self.result_tabs.setCurrentIndex(0) # Discard tables from the previous assessment. Populate detailed tabs on demand. @@ -432,9 +462,17 @@ class MainWindow(QMainWindow): elif key == 'checks': rows = [{'check': name, **value} for name, value in self.data['checks'].items()] elif key in ('risk', 'dependencies'): - rows = self.assessment['findings'] + findings = self.assessment['findings'] + if key == 'dependencies': + findings = [row for row in findings if row['classification'] in ('Confirmed workload dependency','Potential workload dependency')] + rows = [{name: row[name] for name in ('category','classification','severity','points','determines_final_score','evidence','scoring_reason')} for row in findings] else: rows = self.data['inventory'][key] + if key == 'risk': + area.addWidget(self.label(self.assessment['scoring']['formula'] + '\n' + self.assessment['scoring']['explanation'] + '\nPolicy: ' + self.assessment['scoring']['policy']['name'] + ' · ' + self.assessment['scoring']['policy_id'][:12], muted=True)) + if key == 'network': + observations = self.assessment['network_observations'] + rows = [{**row, 'assessed_direction': observations[index]['direction'], 'direction_basis': observations[index]['direction_basis'], 'activity_classification': observations[index]['classification']} for index,row in enumerate(rows)] if key in self.data['checks']: check = self.data['checks'][key] area.addWidget(self.label(f"{check['status'].replace('_', ' ').title()}: {check['message']}", muted=True)) @@ -455,7 +493,7 @@ class MainWindow(QMainWindow): path += '.' + kind try: (export_html if kind == 'html' else export_json)(path, self.data, self.assessment) - QMessageBox.information(self, 'Report saved', 'Assessment saved locally.') + QMessageBox.information(self, 'Report saved', 'HTML report and separate full inventory JSON saved locally.' if kind == 'html' else 'Assessment and full inventory saved as JSON.') except OSError: QMessageBox.warning(self, 'Save failed', 'Unable to write the report. Check the destination and file permissions.') diff --git a/schemas/inventory-v1.schema.json b/schemas/inventory-v1.schema.json index 6f5bc7f..77b974d 100644 --- a/schemas/inventory-v1.schema.json +++ b/schemas/inventory-v1.schema.json @@ -346,6 +346,21 @@ "number", "null" ] + }, + "observed_open_files": { + "type": "integer", + "minimum": 0 + }, + "observed_client_count": { + "type": "integer", + "minimum": 0 + }, + "activity_source": { + "type": "string" + }, + "activity_observed_at": { + "type": "string", + "format": "date-time" } } } diff --git a/tests/fixtures/scoring/active_file_server.json b/tests/fixtures/scoring/active_file_server.json new file mode 100644 index 0000000..6cd1843 --- /dev/null +++ b/tests/fixtures/scoring/active_file_server.json @@ -0,0 +1,1348 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "file-server.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 3, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 445, + "remote_address": "192.0.2.70", + "remote_port": 55250, + "pid": 4, + "process": "System", + "direction": "Unknown" + } + ], + "tasks": [ + { + "name": "Scheduled maintenance", + "path": "\\Microsoft\\Windows\\Maintenance\\", + "enabled": true, + "state": "Ready", + "triggers": "Daily 03:00" + } + ], + "software": [], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + }, + { + "type": "SMB Share", + "name": "BusinessReports", + "path": "D:\\Reports", + "special": false, + "observed_open_files": 8, + "observed_client_count": 3, + "activity_source": "Get-SmbOpenFile (share-root-correlated)", + "activity_observed_at": "2026-10-01T12:00:00Z" + } + ], + "certificates": [] + } +} diff --git a/tests/fixtures/scoring/active_iis_server.json b/tests/fixtures/scoring/active_iis_server.json new file mode 100644 index 0000000..cb475e5 --- /dev/null +++ b/tests/fixtures/scoring/active_iis_server.json @@ -0,0 +1,1357 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "active-iis.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 3, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + }, + { + "name": "Web-Server", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + }, + { + "name": "W3SVC", + "state": "Running", + "pid": 9001 + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 443, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 9002, + "process": "w3wp", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 443, + "remote_address": "192.0.2.60", + "remote_port": 55150, + "pid": 9002, + "process": "w3wp", + "direction": "Unknown" + } + ], + "tasks": [ + { + "name": "Scheduled maintenance", + "path": "\\Microsoft\\Windows\\Maintenance\\", + "enabled": true, + "state": "Ready", + "triggers": "Daily 03:00" + } + ], + "software": [], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + } + ], + "certificates": [] + } +} diff --git a/tests/fixtures/scoring/active_sql_server.json b/tests/fixtures/scoring/active_sql_server.json new file mode 100644 index 0000000..01982dd --- /dev/null +++ b/tests/fixtures/scoring/active_sql_server.json @@ -0,0 +1,1366 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "active-sql.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 3, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + }, + { + "name": "MSSQLSERVER", + "state": "Running", + "pid": 8000, + "account": "EXAMPLE\\svc_sql" + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 1433, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 8000, + "process": "sqlservr", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 1433, + "remote_address": "192.0.2.50", + "remote_port": 55050, + "pid": 8000, + "process": "sqlservr", + "direction": "Unknown" + } + ], + "tasks": [ + { + "name": "Scheduled maintenance", + "path": "\\Microsoft\\Windows\\Maintenance\\", + "enabled": true, + "state": "Ready", + "triggers": "Daily 03:00" + }, + { + "name": "SQL backup", + "path": "\\Operations\\", + "enabled": true, + "executables": "sqlservr.exe", + "triggers": "Daily 01:00" + } + ], + "software": [ + { + "name": "Microsoft SQL Server Database Engine", + "version": "16.0" + } + ], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + } + ], + "certificates": [] + } +} diff --git a/tests/fixtures/scoring/clean_windows.json b/tests/fixtures/scoring/clean_windows.json new file mode 100644 index 0000000..6fa8dd3 --- /dev/null +++ b/tests/fixtures/scoring/clean_windows.json @@ -0,0 +1,1328 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "clean-windows.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 3, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + } + ], + "tasks": [ + { + "name": "Scheduled maintenance", + "path": "\\Microsoft\\Windows\\Maintenance\\", + "enabled": true, + "state": "Ready", + "triggers": "Daily 03:00" + } + ], + "software": [], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + } + ], + "certificates": [] + } +} diff --git a/tests/fixtures/scoring/domain_controller.json b/tests/fixtures/scoring/domain_controller.json new file mode 100644 index 0000000..1550396 --- /dev/null +++ b/tests/fixtures/scoring/domain_controller.json @@ -0,0 +1,1337 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "domain-controller.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 2, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + }, + { + "name": "AD-Domain-Services", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + }, + { + "name": "NTDS", + "state": "Running", + "pid": 10000 + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + } + ], + "tasks": [ + { + "name": "Scheduled maintenance", + "path": "\\Microsoft\\Windows\\Maintenance\\", + "enabled": true, + "state": "Ready", + "triggers": "Daily 03:00" + } + ], + "software": [], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + } + ], + "certificates": [] + } +} diff --git a/tests/fixtures/scoring/inactive_game_server.json b/tests/fixtures/scoring/inactive_game_server.json new file mode 100644 index 0000000..23a6fbf --- /dev/null +++ b/tests/fixtures/scoring/inactive_game_server.json @@ -0,0 +1,1369 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "inactive-game.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 3, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + }, + { + "name": "valheim", + "state": "Stopped", + "startup": "Disabled", + "pid": 0 + }, + { + "name": "Steam Client Service", + "display_name": "Steam Client Service", + "state": "Running", + "pid": 7000 + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 27036, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 7000, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55100, + "remote_address": "198.51.100.30", + "remote_port": 27015, + "pid": 7000, + "process": "steam", + "direction": "Unknown" + } + ], + "tasks": [ + { + "name": "Scheduled maintenance", + "path": "\\Microsoft\\Windows\\Maintenance\\", + "enabled": true, + "state": "Ready", + "triggers": "Daily 03:00" + } + ], + "software": [ + { + "name": "Valheim Dedicated Server (former installation)", + "version": "1.0" + }, + { + "name": "Steam", + "version": "1.0" + } + ], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + } + ], + "certificates": [] + } +} diff --git a/tests/fixtures/scoring/incomplete_collection.json b/tests/fixtures/scoring/incomplete_collection.json new file mode 100644 index 0000000..20fe275 --- /dev/null +++ b/tests/fixtures/scoring/incomplete_collection.json @@ -0,0 +1,1336 @@ +{ + "schema_version": "1.0", + "synthetic": true, + "collected_at": "2026-10-01T12:00:00Z", + "server": { + "hostname": "incomplete.example.test", + "operating_system": "Microsoft Windows Server 2025 Standard (synthetic)", + "os_build": "26100", + "last_boot": "2026-09-25T06:30:00Z", + "ip_addresses": [ + "192.0.2.20" + ], + "network_adapters": [ + "Synthetic Ethernet adapter" + ], + "domain": "example.test", + "platform": "windows", + "os_version": "2025", + "kernel_version": "10.0.26100", + "product_type": 3, + "domain_joined": true, + "collection_source": "Synthetic scoring regression fixture" + }, + "checks": { + "system": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "roles": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "services": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "network": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "tasks": { + "status": "failed", + "message": "Synthetic access denied; no replacement evidence.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [ + { + "code": "access_denied", + "scope": "tasks", + "message": "Current token cannot read this scope." + } + ], + "permission_limitations": [ + "Restricted read access." + ], + "limitations": [ + "Synthetic fixture only." + ] + }, + "software": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "storage": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "shares": { + "status": "complete", + "message": "Synthetic contract fixture; no Windows collection performed.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [], + "permission_limitations": [], + "limitations": [ + "Synthetic fixture only." + ] + }, + "certificates": { + "status": "failed", + "message": "Synthetic access denied; no replacement evidence.", + "started_at": "2026-10-01T12:00:00Z", + "completed_at": "2026-10-01T12:00:01Z", + "errors": [ + { + "code": "access_denied", + "scope": "certificates", + "message": "Current token cannot read this scope." + } + ], + "permission_limitations": [ + "Restricted read access." + ], + "limitations": [ + "Synthetic fixture only." + ] + } + }, + "inventory": { + "roles": [ + { + "name": "NET-Framework-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-Core", + "state": "Installed" + }, + { + "name": "NET-Framework-45-ASPNET", + "state": "Installed" + }, + { + "name": "PowerShell", + "state": "Installed" + }, + { + "name": "PowerShell-V2", + "state": "Installed" + }, + { + "name": "PowerShell-ISE", + "state": "Installed" + }, + { + "name": "Windows-Defender", + "state": "Installed" + }, + { + "name": "Storage-Services", + "state": "Installed" + }, + { + "name": "File-Services", + "state": "Installed" + }, + { + "name": "FS-FileServer", + "state": "Installed" + }, + { + "name": "WoW64-Support", + "state": "Installed" + }, + { + "name": "RSAT", + "state": "Installed" + }, + { + "name": "Telnet-Client", + "state": "Installed" + }, + { + "name": "Windows-Search", + "state": "Installed" + } + ], + "services": [ + { + "name": "bfe", + "display_name": "bfe", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 100 + }, + { + "name": "bits", + "display_name": "bits", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 101 + }, + { + "name": "brokerinfrastructure", + "display_name": "brokerinfrastructure", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 102 + }, + { + "name": "cbdhsvc", + "display_name": "cbdhsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 103 + }, + { + "name": "cdpsusersvc", + "display_name": "cdpsusersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 104 + }, + { + "name": "cdpsvc", + "display_name": "cdpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 105 + }, + { + "name": "clipsvc", + "display_name": "clipsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 106 + }, + { + "name": "coremessagingregistrar", + "display_name": "coremessagingregistrar", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 107 + }, + { + "name": "cryptsvc", + "display_name": "cryptsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 108 + }, + { + "name": "dcomlaunch", + "display_name": "dcomlaunch", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 109 + }, + { + "name": "defragsvc", + "display_name": "defragsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 110 + }, + { + "name": "deviceassociationservice", + "display_name": "deviceassociationservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 111 + }, + { + "name": "dhcp", + "display_name": "dhcp", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 112 + }, + { + "name": "diagnosticshub.standardcollector.service", + "display_name": "diagnosticshub.standardcollector.service", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 113 + }, + { + "name": "diagtrack", + "display_name": "diagtrack", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 114 + }, + { + "name": "dnscache", + "display_name": "dnscache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 115 + }, + { + "name": "dps", + "display_name": "dps", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 116 + }, + { + "name": "dusmsvc", + "display_name": "dusmsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 117 + }, + { + "name": "eventlog", + "display_name": "eventlog", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 118 + }, + { + "name": "eventsystem", + "display_name": "eventsystem", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 119 + }, + { + "name": "fdphost", + "display_name": "fdphost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 120 + }, + { + "name": "fdrespub", + "display_name": "fdrespub", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 121 + }, + { + "name": "fontcache", + "display_name": "fontcache", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 122 + }, + { + "name": "gpsvc", + "display_name": "gpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 123 + }, + { + "name": "hidserv", + "display_name": "hidserv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 124 + }, + { + "name": "hvhost", + "display_name": "hvhost", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 125 + }, + { + "name": "iphlpsvc", + "display_name": "iphlpsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 126 + }, + { + "name": "keyiso", + "display_name": "keyiso", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 127 + }, + { + "name": "ktmrm", + "display_name": "ktmrm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 128 + }, + { + "name": "lanmanserver", + "display_name": "lanmanserver", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 129 + }, + { + "name": "lanmanworkstation", + "display_name": "lanmanworkstation", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 130 + }, + { + "name": "lfsvc", + "display_name": "lfsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 131 + }, + { + "name": "licensemanager", + "display_name": "licensemanager", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 132 + }, + { + "name": "lmhosts", + "display_name": "lmhosts", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 133 + }, + { + "name": "lsm", + "display_name": "lsm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 134 + }, + { + "name": "mpssvc", + "display_name": "mpssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 135 + }, + { + "name": "msdtc", + "display_name": "msdtc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 136 + }, + { + "name": "netlogon", + "display_name": "netlogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 137 + }, + { + "name": "netman", + "display_name": "netman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 138 + }, + { + "name": "netprofm", + "display_name": "netprofm", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 139 + }, + { + "name": "nlasvc", + "display_name": "nlasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 140 + }, + { + "name": "nsi", + "display_name": "nsi", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 141 + }, + { + "name": "pcasvc", + "display_name": "pcasvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 142 + }, + { + "name": "plugplay", + "display_name": "plugplay", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 143 + }, + { + "name": "policyagent", + "display_name": "policyagent", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 144 + }, + { + "name": "power", + "display_name": "power", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 145 + }, + { + "name": "profsvc", + "display_name": "profsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 146 + }, + { + "name": "rasman", + "display_name": "rasman", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 147 + }, + { + "name": "remoteaccess", + "display_name": "remoteaccess", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 148 + }, + { + "name": "remoteregistry", + "display_name": "remoteregistry", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 149 + }, + { + "name": "rpceptmapper", + "display_name": "rpceptmapper", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 150 + }, + { + "name": "rpcss", + "display_name": "rpcss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 151 + }, + { + "name": "samss", + "display_name": "samss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 152 + }, + { + "name": "schedule", + "display_name": "schedule", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 153 + }, + { + "name": "seclogon", + "display_name": "seclogon", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 154 + }, + { + "name": "securityhealthservice", + "display_name": "securityhealthservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 155 + }, + { + "name": "sens", + "display_name": "sens", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 156 + }, + { + "name": "sense", + "display_name": "sense", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 157 + }, + { + "name": "sessionenv", + "display_name": "sessionenv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 158 + }, + { + "name": "shellhwdetection", + "display_name": "shellhwdetection", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 159 + }, + { + "name": "spooler", + "display_name": "spooler", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 160 + }, + { + "name": "sppsvc", + "display_name": "sppsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 161 + }, + { + "name": "sqlwriter", + "display_name": "sqlwriter", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 162 + }, + { + "name": "ssdpsrv", + "display_name": "ssdpsrv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 163 + }, + { + "name": "stisvc", + "display_name": "stisvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 164 + }, + { + "name": "storsvc", + "display_name": "storsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 165 + }, + { + "name": "swprv", + "display_name": "swprv", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 166 + }, + { + "name": "sysmain", + "display_name": "sysmain", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 167 + }, + { + "name": "systemeventsbroker", + "display_name": "systemeventsbroker", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 168 + }, + { + "name": "termservice", + "display_name": "termservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 169 + }, + { + "name": "themes", + "display_name": "themes", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 170 + }, + { + "name": "timebrokersvc", + "display_name": "timebrokersvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 171 + }, + { + "name": "trustedinstaller", + "display_name": "trustedinstaller", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 172 + }, + { + "name": "ualsvc", + "display_name": "ualsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 173 + }, + { + "name": "uhssvc", + "display_name": "uhssvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 174 + }, + { + "name": "umrdpservice", + "display_name": "umrdpservice", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 175 + }, + { + "name": "usosvc", + "display_name": "usosvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 176 + }, + { + "name": "vds", + "display_name": "vds", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 177 + }, + { + "name": "vmicheartbeat", + "display_name": "vmicheartbeat", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 178 + }, + { + "name": "vmickvpexchange", + "display_name": "vmickvpexchange", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 179 + }, + { + "name": "vmicshutdown", + "display_name": "vmicshutdown", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 180 + }, + { + "name": "vmictimesync", + "display_name": "vmictimesync", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 181 + }, + { + "name": "vmicvss", + "display_name": "vmicvss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 182 + }, + { + "name": "vss", + "display_name": "vss", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 183 + }, + { + "name": "w32time", + "display_name": "w32time", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 184 + }, + { + "name": "w3logsvc", + "display_name": "w3logsvc", + "state": "Running", + "startup": "Auto", + "account": "LocalSystem", + "pid": 185 + } + ], + "network": [ + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 135, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 139, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 445, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 3389, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 5985, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49152, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49153, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49154, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49155, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49156, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49157, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49158, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49159, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49160, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49161, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49162, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49163, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49164, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49165, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49166, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49167, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49168, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Listen", + "local_address": "0.0.0.0", + "local_port": 49169, + "remote_address": "0.0.0.0", + "remote_port": 0, + "pid": 4, + "process": "System", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55000, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4000, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55001, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4001, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55002, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4002, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55003, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4003, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55004, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4004, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55005, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4005, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55006, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4006, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55007, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4007, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55008, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4008, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55009, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4009, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55010, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4010, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55011, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4011, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55012, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4012, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55013, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4013, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55014, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4014, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55015, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4015, + "process": "chrome", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55016, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4016, + "process": "steam", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55017, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4017, + "process": "svchost", + "direction": "Unknown" + }, + { + "state": "Established", + "local_address": "192.0.2.20", + "local_port": 55018, + "remote_address": "198.51.100.20", + "remote_port": 443, + "pid": 4018, + "process": "chrome", + "direction": "Unknown" + } + ], + "tasks": [], + "software": [], + "storage": [ + { + "type": "SMB Share", + "name": "ADMIN$", + "path": "C:\\Windows", + "special": true + }, + { + "type": "SMB Share", + "name": "C$", + "path": "C:\\", + "special": true + }, + { + "type": "SMB Share", + "name": "IPC$", + "path": "", + "special": true + } + ], + "certificates": [] + } +} diff --git a/tests/powershell/Test-Collectors.ps1 b/tests/powershell/Test-Collectors.ps1 index 3ac9abb..1291bef 100644 --- a/tests/powershell/Test-Collectors.ps1 +++ b/tests/powershell/Test-Collectors.ps1 @@ -33,6 +33,11 @@ function Get-ScheduledTask { } function Get-Volume { [CmdletBinding()] param() [pscustomobject]@{ FileSystemLabel='Synthetic OS'; DriveLetter='C'; Path='C:\'; FileSystem='NTFS'; Size=100GB; SizeRemaining=50GB } } function Get-SmbShare { [CmdletBinding()] param() [pscustomobject]@{ Name='SyntheticReports'; Path='C:\Reports'; Special=$false } } +function Get-SmbOpenFile { + [CmdletBinding()] param() + [pscustomobject]@{ Path='C:\Reports\SECRET-file.txt'; ShareRelativePath='SECRET-file.txt'; ClientComputerName='192.0.2.21'; ClientUserName='SECRET' } + [pscustomobject]@{ Path='C:\Reports\SECRET-admin.txt'; ShareRelativePath='Reports\SECRET-admin.txt'; ClientComputerName='192.0.2.21'; ClientUserName='SECRET' } +} function Get-ChildItem { [CmdletBinding()] param($LiteralPath) if ($LiteralPath -eq 'Cert:\LocalMachine') { [pscustomobject]@{ PSPath='Cert:\LocalMachine\My'; PSChildName='My' } } @@ -51,6 +56,7 @@ foreach ($name in $collectors.Keys) { Assert-True ($section.started_at -and $section.completed_at) 'Missing timestamps' Assert-True (($section | ConvertTo-Json -Depth 12) -notmatch 'SECRET') 'Secret field serialized' if ($name -eq 'network') { Assert-True ($section.data[0].process -eq 'SyntheticWorker') 'Process correlation failed' } + if ($name -eq 'shares') { Assert-True ($section.data[0].observed_open_files -eq 1) 'Share activity correlation missing' } if ($name -eq 'tasks') { Assert-True ($section.data[0].executables -eq 'report.exe') 'Executable basename missing' } } foreach ($build in @('14393','17763','20348','26100')) { @@ -69,7 +75,7 @@ $section=Invoke-SamsaraSection 'software' {param($r) Invoke-SamsaraRead $r 'Synt Assert-True ($section.status -eq 'complete' -and $section.data[0].name -eq 'Synthetic application') 'Registry metadata projection failed' function Get-SmbShare { [CmdletBinding()] param() throw [UnauthorizedAccessException]::new('SECRET protected details') } $section=Invoke-SamsaraSection 'shares' {param($r) Get-SamsaraShares $r} -Assert-True ($section.status -eq 'failed') 'Denied collection was not failed' +Assert-True ($section.status -eq 'partial' -and $section.data.Count -eq 0) 'Denied share enumeration was not partial/empty' Assert-True ($section.errors[0].code -eq 'access_denied') 'Permission error not categorized' Assert-True (($section | ConvertTo-Json -Depth 12) -notmatch 'SECRET') 'Raw error exposed' $script:SamsaraIsElevated=$false diff --git a/tests/test_core.py b/tests/test_core.py index e786862..c76246e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -25,7 +25,8 @@ class CoreTests(unittest.TestCase): data = collect_demo(DEMO_HOST) result = assess(data) self.assertTrue(all(f['evidence'] for f in result['findings'])) - self.assertTrue(any(f['classification'] == 'Confirmed observed activity' for f in result['findings'])) + self.assertTrue(any(f['classification'] == 'Potential workload dependency' for f in result['findings'])) + self.assertFalse(any(f['classification'] == 'Confirmed workload dependency' for f in result['findings'])) data['inventory']['network'] = [] self.assertTrue(any('does not prove' in f['evidence'] for f in assess(data)['findings'])) self.assertTrue(any('ad:' in f['evidence'] for f in result['findings'])) diff --git a/tests/test_scoring.py b/tests/test_scoring.py new file mode 100644 index 0000000..b4941c4 --- /dev/null +++ b/tests/test_scoring.py @@ -0,0 +1,215 @@ +import copy +import json +from pathlib import Path +import tempfile +import unittest +from samsara.analyzers.assessment import assess +from samsara.analyzers.dependencies import CONFIRMED, POTENTIAL, STANDARD, UNKNOWN +from samsara.analyzers.policy import load_policy +from samsara.models.inventory import validate_inventory +from samsara.reporting.export import export_html, export_json +from samsara.collectors.importer import load_inventory + + +def fixture(name): + return json.loads(Path('tests/fixtures/scoring',name+'.json').read_text()) + + +def scored(result): + return [row for row in result['findings'] if row['points']>0] + + +class ScoringRegressionTests(unittest.TestCase): + def test_required_scenarios(self): + expected = {'clean_windows':0,'inactive_game_server':0,'active_sql_server':70, + 'active_iis_server':70,'domain_controller':95,'active_file_server':80, + 'incomplete_collection':0} + for name,score in expected.items(): + with self.subTest(name=name): + data=fixture(name) + validate_inventory(data) + before=copy.deepcopy(data) + result=assess(data) + self.assertEqual(result['score'],score) + self.assertEqual(data,before,'Analyzer mutated the raw inventory') + self.assertTrue(result['review_required']) + self.assertIn('Historical usage validation',result['manual_verification']) + self.assertTrue(all(f['evidence'] and f['scoring_reason'] for f in result['findings'])) + self.assertEqual(result['score'],max((r['score'] for r in result['scoring']['breakdown']),default=0)) + self.assertTrue(all(0<=f['points']<=100 for f in result['findings'])) + + def test_original_1565_point_incident_is_not_a_workload(self): + data=fixture('incomplete_collection') + inventory=data['inventory'] + self.assertEqual(len(inventory['services']),86) + self.assertEqual(len(inventory['roles']),14) + self.assertEqual(sum(r['state']=='Established' for r in inventory['network']),19) + self.assertEqual(sum(r['state']=='Listen' for r in inventory['network']),23) + self.assertEqual(len(inventory['storage']),3) + old_points=86*10+19*20+14*10+23*5+3*15+2*10+5 + self.assertEqual(old_points,1565) + result=assess(data) + self.assertEqual(result['score'],0) + self.assertFalse(scored(result)) + self.assertLess(len(result['findings']),15) + self.assertLess(result['coverage']['percent'],100) + + def test_standard_os_services_features_and_admin_shares_stay_visible(self): + data=fixture('clean_windows') + result=assess(data) + standard=[r for r in result['findings'] if r['classification']==STANDARD] + self.assertTrue(standard) + self.assertTrue(all(r['points']==0 for r in standard)) + for share in data['inventory']['storage']: + share.update(observed_open_files=100,activity_source='Get-SmbOpenFile (share-root-correlated)') + self.assertEqual(assess(data)['score'],0) + # A hidden business share is not a default administrative share. + data['inventory']['storage'].append({'type':'SMB Share','name':'Finance$','path':'D:\\Finance','special':True}) + self.assertEqual(assess(data)['score'],20) + + def test_running_service_listener_and_connections_are_one_workload(self): + data=fixture('active_sql_server') + baseline=assess(data) + self.assertEqual(len(scored(baseline)),1) + self.assertEqual(scored(baseline)[0]['id'],'sql') + self.assertEqual(scored(baseline)[0]['classification'],CONFIRMED) + data['inventory']['network'] *= 20 + data['inventory']['services'] *= 10 + repeated=assess(data) + self.assertEqual(repeated['score'],70) + self.assertEqual(len(scored(repeated)),1) + selected=repeated['scoring']['breakdown'][0] + self.assertIn('observed_application_clients',[r['selected_rule'] for r in repeated['scoring']['breakdown']]) + self.assertTrue(selected['selected_evidence_refs']) + self.assertIn('Observed application TCP activity',scored(repeated)[0]['evidence']) + + def test_independent_workloads_use_maximum_not_sum(self): + data=fixture('active_sql_server') + other=fixture('active_file_server')['inventory']['storage'][-1] + data['inventory']['storage'].append(other) + result=assess(data) + self.assertEqual(sorted(r['score'] for r in result['scoring']['breakdown']),[70,80]) + self.assertEqual(result['score'],80) + self.assertEqual(sum(r['determines_final_score'] for r in result['scoring']['breakdown']),1) + + def test_coverage_does_not_inflate_or_mask_known_risk(self): + clean=assess(fixture('clean_windows')) + incomplete=assess(fixture('incomplete_collection')) + self.assertEqual(clean['score'],incomplete['score']) + self.assertLess(incomplete['coverage']['percent'],clean['coverage']['percent']) + self.assertEqual(clean['confidence']['level'],'high') + self.assertEqual(incomplete['confidence']['level'],'moderate') + data=fixture('active_sql_server') + for check in data['checks'].values(): + check['status']='failed' + result=assess(data) + self.assertEqual(result['score'],70,'Missing scope cannot erase actual captured positive evidence') + self.assertEqual(result['confidence']['level'],'low') + self.assertEqual(result['coverage']['percent'],0) + self.assertTrue(all(f['points']==0 for f in result['findings'] if f['category']=='Missing evidence')) + + def test_browser_steam_loopback_outbound_and_unknown_are_not_inbound(self): + self.assertFalse(any(r['id']=='game-server' for r in assess(fixture('inactive_game_server'))['findings'])) + data=fixture('active_sql_server') + # Remove active external connections, keep the running service/listener. + row=data['inventory']['network'][-1] + row['remote_address']='127.0.0.1' + result=assess(data) + self.assertEqual(result['score'],45) + self.assertEqual(result['network_observations'][-1]['direction'],'Loopback / same host') + self.assertFalse(any(f['classification']==CONFIRMED for f in scored(result))) + row['remote_address']='::ffff:127.0.0.1' + self.assertEqual(assess(data)['network_observations'][-1]['direction'],'Loopback / same host') + row.update(remote_address='192.0.2.50',direction='Outbound') + self.assertEqual(assess(data)['score'],45) + row.update(pid=99999,direction='Unknown',local_port=1433) + self.assertEqual(assess(data)['network_observations'][-1]['direction'],'Unknown') + self.assertEqual(assess(data)['score'],45) + row.update(local_port=55200,remote_port=443) + self.assertEqual(assess(data)['network_observations'][-1]['direction'],'Outbound (inferred)') + + def test_port_alone_and_iis_http_sys_are_not_proof_of_a_hosted_app(self): + data=fixture('clean_windows') + data['inventory']['network'].append({'state':'Listen','local_address':'0.0.0.0','local_port':27015,'pid':12345,'process':'unknown','remote_address':'','remote_port':0,'direction':'Unknown'}) + self.assertEqual(assess(data)['score'],0) + data=fixture('active_iis_server') + for row in data['inventory']['network'][-2:]: + row.update(pid=4,process='System') + result=assess(data) + self.assertEqual(result['score'],35) + self.assertFalse(any(f['classification']==CONFIRMED for f in scored(result))) + + def test_role_only_not_domain_controller_and_smb_transport_not_share_use(self): + data=fixture('domain_controller');data['server']['product_type']=3 + data['inventory']['services']=[r for r in data['inventory']['services'] if r['name']!='NTDS'] + self.assertEqual(assess(data)['score'],15) + data=fixture('active_file_server') + share=data['inventory']['storage'][-1] + share['activity_source']='Get-SmbOpenFile (administrative alias ambiguous)' + self.assertEqual(assess(data)['score'],40) + share.pop('observed_open_files') + result=assess(data) + self.assertEqual(result['score'],40) + self.assertEqual(scored(result)[0]['classification'],POTENTIAL) + self.assertIn('accessed share is unknown',scored(result)[0]['evidence']) + + def test_policy_validation_configuration_and_audit_snapshot(self): + policy=load_policy() + policy['scores']['observed_application_clients']=55 + result=assess(fixture('active_sql_server'),policy=policy) + self.assertEqual(result['score'],55) + self.assertEqual(result['scoring']['policy']['scores']['observed_application_clients'],55) + policy['scores']['observed_application_clients']=2 + self.assertEqual(result['scoring']['policy']['scores']['observed_application_clients'],55) + for source in [[],{},dict(load_policy(),model_version='1.0')]: + with self.subTest(source=type(source)),self.assertRaises(ValueError): + load_policy(source) + bad=load_policy();bad['scores']['running_application_service']=-1 + with self.assertRaises(ValueError): load_policy(bad) + bad=load_policy();bad['thresholds']['medium']=90 + with self.assertRaises(ValueError): load_policy(bad) + + def test_actionable_html_and_separate_complete_inventory(self): + data=fixture('clean_windows') + with tempfile.TemporaryDirectory() as folder: + report=Path(folder)/'assessment.html' + export_html(report,data,assess(data)) + text=report.read_text() + self.assertIn('How the score was calculated',text) + self.assertIn('Evidence confidence',text) + self.assertIn('Historical usage validation',text) + self.assertNotIn('medium-risk dependencies',text) + self.assertLess(text.count(''),60) + inventory=list(Path(folder).glob('*.inventory-*.json')) + self.assertEqual(len(inventory),1) + raw=json.loads(inventory[0].read_text()) + self.assertEqual(raw,data) + self.assertEqual(len(raw['inventory']['services']),86) + # Re-export reuses an identical artifact rather than losing inventory. + export_html(report,data,assess(data)) + export_json(Path(folder)/'assessment.json',data,assess(data)) + self.assertEqual(load_inventory(inventory[0])['inventory'],load_inventory(Path(folder)/'assessment.json')['inventory']) + + def test_custom_jobs_not_misclassified_by_substrings_and_zero_points_maintenance(self): + data=fixture('clean_windows') + data['inventory']['tasks'].append({'name':'capture-report','path':'/etc/cron.d/custom','enabled':True,'triggers':'0 2 * * *'}) + self.assertEqual(assess(data)['score'],20) + data['inventory']['tasks'][-1]['enabled']=False + self.assertEqual(assess(data)['score'],0) + data['inventory']['tasks'][-1].update(name='Business export',path='\\Microsoft\\Windows\\PowerShell\\ScheduledJobs\\',enabled=True) + self.assertEqual(assess(data)['score'],20) + + def test_html_escapes_actionable_evidence_and_artifacts_are_collision_safe(self): + data=fixture('active_sql_server') + data['inventory']['services'][-1]['account']='' + result=assess(data) + with tempfile.TemporaryDirectory() as folder: + report=Path(folder)/'assessment.html' + export_html(report,data,result) + self.assertNotIn('