param( [switch] $PlanOnly, [switch] $Force ) $ErrorActionPreference = 'Stop' $repoRoot = (& git rev-parse --show-toplevel).Trim() if (-not $repoRoot) { throw 'This script must be run from inside the Blackbird git repository.' } Set-Location $repoRoot $privateOrExternalPaths = @( 'Blackbird.code-workspace', 'Client/analysis/Rules/SignatureIntel/signature-rules.json', 'Client/analysis/Rules/SignatureIntel/default-sigma.yml', 'Client/analysis/Rules/SignatureIntel/Bundled/', 'Docs/', 'docs/', 'Yara/', 'Server/', 'Lib/NetworkServiceLayer/', 'Kernel/network/', 'Kernel/core/crashdump.c', 'Kernel/monitors/bugcheck_monitor.c', 'Kernel/monitors/bugcheck_monitor.h', 'UserMode/netsvc/', 'Usermode/netsvc/', 'VCXProj/BlackbirdNetSvc.vcxproj', 'VCXProj/BlackbirdNetSvc.vcxproj.filters' ) function Convert-ToGitPath { param([string] $Path) return ($Path -replace '\\', '/').TrimStart('./') } function Test-SkippedPath { param([string] $Path) $gitPath = Convert-ToGitPath $Path foreach ($skipPath in $privateOrExternalPaths) { $skip = Convert-ToGitPath $skipPath if ($skip.EndsWith('/')) { if ($gitPath.StartsWith($skip, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } } elseif ($gitPath.Equals($skip, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } } return $false } function Test-TrackedPath { param([string] $Path) $gitPath = Convert-ToGitPath $Path $tracked = @(& git ls-files -- $Path 2>$null | ForEach-Object { Convert-ToGitPath $_ }) if ($gitPath.EndsWith('/')) { $prefix = $gitPath } else { $prefix = "$gitPath/" } foreach ($trackedPath in $tracked) { if ($trackedPath -ceq $gitPath) { return $true } if ($trackedPath.StartsWith($prefix, [System.StringComparison]::Ordinal)) { return $true } } return $false } function Test-ExactExistingPath { param([string] $Path) $requestedGitPath = Convert-ToGitPath $Path if ([string]::IsNullOrWhiteSpace($requestedGitPath)) { return $false } $current = $repoRoot foreach ($segment in $requestedGitPath.Split('/')) { if ([string]::IsNullOrWhiteSpace($segment)) { continue } $match = Get-ChildItem -LiteralPath $current -Force -ErrorAction SilentlyContinue | Where-Object { $_.Name -ceq $segment } | Select-Object -First 1 if ($null -eq $match) { return $false } $current = $match.FullName } return $true } function Get-StageablePaths { param([string[]] $Paths) $stageable = New-Object System.Collections.Generic.List[string] foreach ($path in $Paths) { if (Test-SkippedPath $path) { Write-Warning "Skipping private/external path: $path" continue } if ((Test-ExactExistingPath $path) -or (Test-TrackedPath $path)) { [void] $stageable.Add($path) } else { Write-Host "Skipping missing path: $path" } } return $stageable.ToArray() } function Test-AnyStagedChanges { & git diff --cached --quiet return ($LASTEXITCODE -ne 0) } function Commit-Group { param( [string] $Message, [string[]] $Paths ) $stageable = Get-StageablePaths $Paths if ($stageable.Count -eq 0) { Write-Host "No paths for: $Message" return } if ($PlanOnly) { Write-Host '' Write-Host "Would commit: $Message" foreach ($path in $stageable) { Write-Host " $path" } return } foreach ($path in $stageable) { & git add -A -- $path if ($LASTEXITCODE -ne 0) { throw "git add failed for commit group '$Message' path '$path'" } } if (-not (Test-AnyStagedChanges)) { Write-Host "No staged changes for: $Message" return } & git commit -m $Message if ($LASTEXITCODE -ne 0) { throw "git commit failed for: $Message" } } if (-not $PlanOnly) { if ((Test-AnyStagedChanges) -and -not $Force) { throw 'The git index already has staged changes. Commit or unstage them first, or pass -Force to continue.' } } $groups = @( @{ Message = 'repo/policy: clarify defensive public scope' Paths = @('.gitignore', 'README.md', 'LICENSE') }, @{ Message = 'build/style: move clang-format into vcxproj configuration' Paths = @('.clang-format', 'VCXProj/.clang-format') }, @{ Message = 'abi: normalize IPC contract header name' Paths = @('ABI/Blackbird_ipc.h', 'ABI/blackbird_ipc.h') }, @{ Message = 'abi: add diagnostics and launch subject contracts' Paths = @('ABI/blackbird_ioctl.h', 'ABI/blackbird_ipc.h') }, @{ Message = 'kernel/callbacks: move OS callback monitors into callbacks tree' Paths = @( 'Kernel/monitors/filesystem_monitor.c', 'Kernel/monitors/filesystem_monitor.h', 'Kernel/monitors/handle_monitor.c', 'Kernel/monitors/handle_monitor.h', 'Kernel/monitors/image_monitor.c', 'Kernel/monitors/image_monitor.h', 'Kernel/monitors/process_monitor.c', 'Kernel/monitors/process_monitor.h', 'Kernel/monitors/registry_monitor.c', 'Kernel/monitors/registry_monitor.h', 'Kernel/monitors/thread_monitor.c', 'Kernel/monitors/thread_monitor.h', 'Kernel/callbacks' ) }, @{ Message = 'kernel/core: add optional private feature boundaries' Paths = @( 'Kernel/core/optional_features.h', 'Kernel/core/optional_feature_stubs.c', 'Kernel/core/crashdump.h' ) }, @{ Message = 'kernel/core: add diagnostics component registry' Paths = @('Kernel/core/diagnostics.c', 'Kernel/core/diagnostics.h') }, @{ Message = 'kernel/core: integrate optional subsystem lifecycle' Paths = @( 'Kernel/core/driver.c', 'Kernel/core/control/control_common.c', 'Kernel/core/control/control_dispatch_init.c', 'Kernel/core/control/control_ioctl_handlers.c', 'Kernel/core/control/control_private.h', 'Kernel/core/control/control_uninit_exports.c' ) }, @{ Message = 'kernel/core: extend runtime configuration state' Paths = @('Kernel/core/runtime_config.c', 'Kernel/core/runtime_config.h', 'Kernel/core/control.h') }, @{ Message = 'kernel/core: refresh Tempus debug and utility helpers' Paths = @( 'Kernel/core/tempus_debug.c', 'Kernel/core/tempus_debug.h', 'Kernel/core/pool_compat.h', 'Kernel/core/protection_utils.h', 'Kernel/core/unicode_utils.h' ) }, @{ Message = 'kernel/antivirt: add QPC timing compensation' Paths = @('Kernel/antivirt/qpc_timing.c', 'Kernel/antivirt/qpc_timing.h', 'Kernel/antivirt/antivirt_ntapi_firmware.c') }, @{ Message = 'kernel/antivirt: harden registry concealment flow' Paths = @('Kernel/antivirt/registry_concealment.c', 'Kernel/antivirt/registry_concealment.h') }, @{ Message = 'kernel/correlation: extend hollowing analysis state' Paths = @('Kernel/correlation/hollowing_engine.c', 'Kernel/correlation/hollowing_engine.h') }, @{ Message = 'kernel/correlation: refresh intent tracking store' Paths = @('Kernel/correlation/intent_store.c', 'Kernel/correlation/intent_store.h') }, @{ Message = 'kernel/hooks: update NT hook stubs and resolver' Paths = @( 'Kernel/hooks/asm/ntapi_stubs.asm', 'Kernel/hooks/hook/ntapi_hook.c', 'Kernel/hooks/hook/ntapi_hook.h', 'Kernel/hooks/hook/ntapi_hook_ldasm.c', 'Kernel/hooks/hook/ntapi_hook_ldasm.h', 'Kernel/hooks/hook/ntapi_hook_resolve.c' ) }, @{ Message = 'kernel/ntapi: expand NT monitor coverage' Paths = @( 'Kernel/hooks/monitor/ntapi_monitor.c', 'Kernel/hooks/monitor/ntapi_monitor.h', 'Kernel/hooks/monitor/ntapi_monitor_hooks.c', 'Kernel/hooks/monitor/ntapi_monitor_private.h' ) }, @{ Message = 'kernel/monitors: update anti-tamper and APC monitors' Paths = @('Kernel/monitors/anti_tamper.c', 'Kernel/monitors/anti_tamper.h', 'Kernel/monitors/apc_monitor.c', 'Kernel/monitors/apc_monitor.h') }, @{ Message = 'kernel/telemetry: refresh ETW telemetry bridge' Paths = @('Kernel/telemetry/etw.c', 'Kernel/telemetry/etw.h') }, @{ Message = 'kernel/include: add native PEB and TEB declarations' Paths = @('Kernel/include') }, @{ Message = 'controller/core: normalize private headers' Paths = @('UserMode/controller/core/Blackbird_controller_private.h', 'UserMode/controller/core/controller_private.h') }, @{ Message = 'controller/core: refresh controller entrypoint logging' Paths = @('UserMode/controller/Blackbird_controller.cpp', 'UserMode/controller/controller.cpp') }, @{ Message = 'controller/injection: split launch helper modules' Paths = @( 'UserMode/controller/core/injection/Blackbird_controller_injection.cpp', 'UserMode/controller/core/injection/Blackbird_controller_injection.h', 'UserMode/controller/core/injection/common.cpp', 'UserMode/controller/core/injection/environment.cpp', 'UserMode/controller/core/injection/hook.cpp', 'UserMode/controller/core/injection/image.cpp', 'UserMode/controller/core/injection/injection.cpp', 'UserMode/controller/core/injection/injection.h', 'UserMode/controller/core/injection/internal.h', 'UserMode/controller/core/injection/launch.cpp', 'UserMode/controller/core/injection/token.cpp' ) }, @{ Message = 'controller/ipc: split pipe protocol handlers' Paths = @( 'UserMode/controller/core/ipc/Blackbird_controller_ipc.cpp', 'UserMode/controller/core/ipc/ipc.cpp', 'UserMode/controller/core/ipc/ipc_hook_support.cpp', 'UserMode/controller/core/ipc/ipc_internal.h', 'UserMode/controller/core/ipc/ipc_launch.cpp', 'UserMode/controller/core/ipc/ipc_proxy.cpp', 'UserMode/controller/core/ipc/ipc_shared_ring.cpp' ) }, @{ Message = 'controller/monitoring: clean ETW and subscription modules' Paths = @( 'UserMode/controller/core/monitoring/Blackbird_controller_etw_monitor.cpp', 'UserMode/controller/core/monitoring/Blackbird_controller_subscriptions.cpp', 'UserMode/controller/core/monitoring/etw_monitor.cpp', 'UserMode/controller/core/monitoring/subscriptions.cpp', 'UserMode/controller/core/monitoring/ubscriptions.cpp' ) }, @{ Message = 'controller/runtime: split service runtime and symbols' Paths = @( 'UserMode/controller/core/runtime/Blackbird_controller_runtime.cpp', 'UserMode/controller/core/runtime/blackbird_controller_symbol_service.cpp', 'UserMode/controller/core/runtime/runtime.cpp', 'UserMode/controller/core/runtime/symbol_service.cpp' ) }, @{ Message = 'controller/netsvc: make network service launcher optional' Paths = @( 'UserMode/controller/core/runtime/blackbird_controller_node_runtime.cpp', 'UserMode/controller/core/runtime/node_runtime.cpp', 'UserMode/controller/core/runtime/ns' ) }, @{ Message = 'controller/correlation: refresh hollowing analysis' Paths = @('UserMode/controller/core/correlation/Blackbird_controller_hollowing.cpp', 'UserMode/controller/core/correlation/hollowing.cpp') }, @{ Message = 'controller/heuristics: refresh event classification' Paths = @( 'UserMode/controller/core/heuristics/Blackbird_controller_heuristics.cpp', 'UserMode/controller/core/heuristics/heuristics.cpp', 'UserMode/controller/core/heuristics/heuristics.h' ) }, @{ Message = 'usermode/dllhost: add DLL analysis host' Paths = @('UserMode/dllhost') }, @{ Message = 'usermode/include: add native PEB helpers' Paths = @('UserMode/include') }, @{ Message = 'hook/runtime: update SR71 bootstrap and launch gate' Paths = @( 'UserMode/hook/dll.cpp', 'UserMode/hook/hooks/runtime_bootstrap.cpp', 'UserMode/hook/hooks/runtime_launch_gate.cpp', 'UserMode/hook/hooks/runtime_private.h' ) }, @{ Message = 'hook/runtime: harden SR71 integrity state' Paths = @('UserMode/hook/hooks/runtime.cpp', 'UserMode/hook/hooks/runtime.h', 'UserMode/hook/hooks/runtime_integrity.cpp') }, @{ Message = 'hook/nt: expand NT hook descriptors' Paths = @('UserMode/hook/hooks/nt.cpp', 'UserMode/hook/hooks/nt.h') }, @{ Message = 'hook/module: update module instrumentation' Paths = @('UserMode/hook/hooks/module.cpp', 'UserMode/hook/hooks/module.h') }, @{ Message = 'hook/ki: update exception dispatcher instrumentation' Paths = @('UserMode/hook/hooks/ki.cpp', 'UserMode/hook/hooks/ki.h') }, @{ Message = 'hook/ws: refresh Winsock telemetry hooks' Paths = @('UserMode/hook/hooks/ws.cpp', 'UserMode/hook/hooks/ws.h') }, @{ Message = 'hook/instrument: update owned range publication' Paths = @( 'UserMode/hook/instrument/bk.cpp', 'UserMode/hook/instrument/bk.h', 'UserMode/hook/instrument/stacktrace.cpp', 'UserMode/hook/instrument/stacktrace.h', 'UserMode/hook/instrument/unlink.cpp' ) }, @{ Message = 'hook/ipc: update hook pipe transport' Paths = @('UserMode/hook/ipc/pipe.cpp', 'UserMode/hook/ipc/pipe.h') }, @{ Message = 'hook/build: refresh SR71 project inputs' Paths = @('UserMode/hook/vcxproj/BlackbirdHook.vcxproj') }, @{ Message = 'sensor: normalize sensor source names' Paths = @( 'UserMode/sensor/Blackbird_etw_printer.c', 'UserMode/sensor/Blackbird_etw_printer.h', 'UserMode/sensor/Blackbird_etw_props.c', 'UserMode/sensor/Blackbird_etw_props.h', 'UserMode/sensor/Blackbird_etw_symbols.c', 'UserMode/sensor/Blackbird_etw_symbols.h', 'UserMode/sensor/Blackbird_event_printer.c', 'UserMode/sensor/Blackbird_event_printer.h', 'UserMode/sensor/Blackbird_ioctl_test.c', 'UserMode/sensor/Blackbird_sensor_core.c', 'UserMode/sensor/Blackbird_sensor_core.h', 'UserMode/sensor/Blackbird_symbol_common.c', 'UserMode/sensor/Blackbird_symbol_common.h', 'UserMode/sensor/Blackbird_symbol_resolver.c', 'UserMode/sensor/Blackbird_symbol_resolver.h', 'UserMode/sensor/Blackbird_test_report_html.c', 'UserMode/sensor/Blackbird_test_report_html.h', 'UserMode/sensor/etw_printer.c', 'UserMode/sensor/etw_printer.h', 'UserMode/sensor/etw_props.c', 'UserMode/sensor/etw_props.h', 'UserMode/sensor/etw_symbols.c', 'UserMode/sensor/etw_symbols.h', 'UserMode/sensor/event_printer.c', 'UserMode/sensor/event_printer.h', 'UserMode/sensor/ioctl_test.c', 'UserMode/sensor/sensor_core.c', 'UserMode/sensor/sensor_core.h', 'UserMode/sensor/symbol_common.c', 'UserMode/sensor/symbol_common.h', 'UserMode/sensor/symbol_resolver.c', 'UserMode/sensor/symbol_resolver.h', 'UserMode/sensor/test_report_html.c', 'UserMode/sensor/test_report_html.h' ) }, @{ Message = 'sensor/core: normalize sensor protocol modules' Paths = @( 'UserMode/sensor/core/Blackbird_sensor_core_etw.c', 'UserMode/sensor/core/Blackbird_sensor_core_internal.h', 'UserMode/sensor/core/Blackbird_sensor_core_protocol.c', 'UserMode/sensor/core/sensor_core_etw.c', 'UserMode/sensor/core/sensor_core_internal.h', 'UserMode/sensor/core/sensor_core_protocol.c' ) }, @{ Message = 'sensor/tests: normalize ioctl test harness' Paths = @( 'UserMode/sensor/tests/Blackbird_ioctl_test_env.c', 'UserMode/sensor/tests/Blackbird_ioctl_test_etw.c', 'UserMode/sensor/tests/Blackbird_ioctl_test_intent.c', 'UserMode/sensor/tests/Blackbird_ioctl_test_internal.h', 'UserMode/sensor/tests/Blackbird_ioctl_test_ioctl.c', 'UserMode/sensor/tests/Blackbird_ioctl_test_main.c', 'UserMode/sensor/tests/Blackbird_ioctl_test_report.c', 'UserMode/sensor/tests/ioctl_test_env.c', 'UserMode/sensor/tests/ioctl_test_etw.c', 'UserMode/sensor/tests/ioctl_test_intent.c', 'UserMode/sensor/tests/ioctl_test_internal.h', 'UserMode/sensor/tests/ioctl_test_ioctl.c', 'UserMode/sensor/tests/ioctl_test_main.c', 'UserMode/sensor/tests/ioctl_test_report.c' ) }, @{ Message = 'sensor/docs: refresh sensor usage notes' Paths = @('UserMode/sensor/README.md') }, @{ Message = 'ui/interop: add backend and native bindings' Paths = @( 'Client/analysis/Interop/BlackbirdNative.Launch.cs', 'Client/analysis/Interop/BlackbirdNative.cs', 'Client/analysis/Interop/BkdcNative.cs', 'Client/analysis/Interop/Kernel32Native.cs' ) }, @{ Message = 'ui/models: add disassembly and trust models' Paths = @( 'Client/analysis/Models/BlackbirdBackendModels.cs', 'Client/analysis/Models/DiagnosticsState.cs', 'Client/analysis/Models/GraphExplorerItem.cs', 'Client/analysis/Models/InspectorModels.cs', 'Client/analysis/Models/LaunchProfile.cs', 'Client/analysis/Models/MemoryDisassemblyRequestedEventArgs.cs', 'Client/analysis/Models/PaneHeaderDragEventArgs.cs', 'Client/analysis/Models/PerformanceSample.cs', 'Client/analysis/Models/SignatureTrustState.cs', 'Client/analysis/Models/StackFrameRow.cs', 'Client/analysis/Models/TelemetryEvent.cs', 'Client/analysis/Models/ThreadStackSessionModels.cs' ) }, @{ Message = 'ui/controls: refresh timeline and performance controls' Paths = @( 'Client/analysis/Controls/BulkObservableCollection.cs', 'Client/analysis/Controls/PerformanceChartControl.cs', 'Client/analysis/Controls/SparklinePreviewControl.cs', 'Client/analysis/Controls/TimeSeriesBuffer.cs', 'Client/analysis/Controls/TimelineControl.cs' ) }, @{ Message = 'ui/services: add launch orchestration service' Paths = @( 'Client/analysis/Services/AnalysisLaunchService.cs', 'Client/analysis/Services/BlackbirdPreflight.cs', 'Client/analysis/Services/BlackbirdServiceControl.cs', 'Client/analysis/Services/LaunchHookOptions.cs' ) }, @{ Message = 'ui/services: add runtime configuration service' Paths = @( 'Client/analysis/Services/RuntimeConfigService.cs', 'Client/analysis/Shell/MainWindow.RuntimeConfig.cs', 'Client/analysis/Shell/MainWindow.RuntimeConfigApi.cs', 'Client/analysis/runtimeconfig.template.json' ) }, @{ Message = 'ui/services: improve backend session lifecycle' Paths = @( 'Client/analysis/Services/BlackbirdBackendSession.cs', 'Client/analysis/Services/BlackbirdControlDeviceSession.cs', 'Client/analysis/Services/BoundedStringPool.cs', 'Client/analysis/Services/DebugConsoleService.cs', 'Client/analysis/Services/OutputCapture.cs' ) }, @{ Message = 'ui/services: add component and operator identity helpers' Paths = @( 'Client/analysis/Services/ComponentIdentityService.cs', 'Client/analysis/Services/OperatorIdentityService.cs', 'Client/analysis/Services/VmRegistrationPackageService.cs' ) }, @{ Message = 'ui/capture: refactor archive storage primitives' Paths = @( 'Client/analysis/Services/Capture/CaptureArchiveModels.cs', 'Client/analysis/Services/Capture/CaptureArchiveStorage.cs', 'Client/analysis/Services/Capture/Lz4BlockCodec.cs', 'Client/analysis/Services/Capture/SqliteDatabase.cs', 'Client/analysis/Services/Capture/SqliteException.cs', 'Client/analysis/Services/Capture/SqliteNative.cs', 'Client/analysis/Services/Capture/SqliteStatement.cs' ) }, @{ Message = 'ui/capture: add live capture projection engine' Paths = @('Client/analysis/Services/Capture/CaptureLiveStoreImpl.cs', 'Client/analysis/Services/Capture/CaptureProjectionEngine.cs') }, @{ Message = 'ui/services: add broker ETW event mapping' Paths = @( 'Client/analysis/Services/BrokerEtwEventMapper.cs', 'Client/analysis/Services/ProcessGraphProjectionBuilder.cs', 'Client/analysis/Services/ProcessIdentityResolver.cs' ) }, @{ Message = 'ui/services: update event formatting and compaction' Paths = @( 'Client/analysis/Services/EventDetailFormatting.cs', 'Client/analysis/Services/EventDetailsParsing.cs', 'Client/analysis/Services/GroupedEventCompaction.cs', 'Client/analysis/Services/GroupedEventPaneState.cs', 'Client/analysis/Services/IntelDetailsProvider.cs', 'Client/analysis/Services/TelemetryEventStore.cs' ) }, @{ Message = 'ui/services: expand performance and stack sampling' Paths = @( 'Client/analysis/Services/PerformanceSampler.cs', 'Client/analysis/Services/ThreadStackResolver.cs', 'Client/analysis/Services/VirtualizationProbe.cs' ) }, @{ Message = 'ui/signature: expand signature intel analysis' Paths = @( 'Client/analysis/Services/SignatureIntelService.cs', 'Client/analysis/Services/PackerDetectionService.cs', 'Client/analysis/Shell/MainWindow.SignatureIntel.cs', 'Client/analysis/Windows/SignatureIntelRulesWindow.xaml', 'Client/analysis/Windows/SignatureIntelRulesWindow.xaml.cs' ) }, @{ Message = 'ui/session: update session storage and export flow' Paths = @( 'Client/analysis/Services/SessionExportService.cs', 'Client/analysis/Services/SessionFileStorage.cs', 'Client/analysis/Shell/MainWindow.CaptureStore.cs', 'Client/analysis/Shell/MainWindow.SessionStorage.cs' ) }, @{ Message = 'ui/shell: rebuild main window backend flow' Paths = @( 'Client/analysis/App.xaml.cs', 'Client/analysis/AssemblyInfo.cs', 'Client/analysis/Shell/MainWindow.Backend.cs', 'Client/analysis/Shell/MainWindow.HooksArm.cs', 'Client/analysis/Shell/MainWindow.Inspectors.cs', 'Client/analysis/Shell/MainWindow.IntelDetailsProvider.cs', 'Client/analysis/Shell/MainWindow.xaml', 'Client/analysis/Shell/MainWindow.xaml.cs' ) }, @{ Message = 'ui/shell: add settings surface' Paths = @( 'Client/analysis/Settings', 'Client/analysis/Shell/MainWindow.Settings.cs', 'Client/analysis/Windows/InterfaceSettingsWindow.xaml', 'Client/analysis/Windows/InterfaceSettingsWindow.xaml.cs', 'Client/analysis/Windows/LaneSettingsWindow.xaml.cs' ) }, @{ Message = 'ui/theme: add light theme and chrome helpers' Paths = @( 'Client/analysis/Themes/DarkTheme.xaml', 'Client/analysis/Themes/LightTheme.xaml', 'Client/analysis/Theming/ThemedMessageBox.cs', 'Client/analysis/Theming/UiPalette.cs', 'Client/analysis/Theming/WindowChromeBehavior.cs', 'Client/analysis/Theming/WindowThemeHelper.cs' ) }, @{ Message = 'ui/panes: refresh ETW and event panes' Paths = @( 'Client/analysis/Panes/EtwPane.xaml', 'Client/analysis/Panes/EtwPane.xaml.cs', 'Client/analysis/Panes/EventsPane.xaml', 'Client/analysis/Panes/EventsPane.xaml.cs' ) }, @{ Message = 'ui/panes: refresh filesystem and heuristics panes' Paths = @( 'Client/analysis/Panes/FilesystemPane.xaml', 'Client/analysis/Panes/FilesystemPane.xaml.cs', 'Client/analysis/Panes/HeuristicsPane.xaml', 'Client/analysis/Panes/HeuristicsPane.xaml.cs' ) }, @{ Message = 'ui/panes: replace IPC uplink pane' Paths = @('Client/analysis/Panes/IpcUplinkPane.xaml', 'Client/analysis/Panes/IpcUplinkPane.xaml.cs') }, @{ Message = 'ui/panes: expand performance pane' Paths = @('Client/analysis/Panes/PerformancePane.xaml', 'Client/analysis/Panes/PerformancePane.xaml.cs') }, @{ Message = 'ui/panes: update process and registry panes' Paths = @( 'Client/analysis/Panes/ProcessRelationsPane.xaml', 'Client/analysis/Panes/ProcessRelationsPane.xaml.cs', 'Client/analysis/Panes/RegistryPane.xaml', 'Client/analysis/Panes/RegistryPane.xaml.cs' ) }, @{ Message = 'ui/windows: update diagnostics and disassembly views' Paths = @( 'Client/analysis/Windows/DiagnosticsWindow.xaml', 'Client/analysis/Windows/DiagnosticsWindow.xaml.cs', 'Client/analysis/Windows/DirectSyscallSuspectWindow.xaml', 'Client/analysis/Windows/DirectSyscallSuspectWindow.xaml.cs', 'Client/analysis/Windows/DisassemblyWindow.xaml', 'Client/analysis/Windows/DisassemblyWindow.xaml.cs' ) }, @{ Message = 'ui/windows: update evidence inspector views' Paths = @( 'Client/analysis/Windows/ChildProcessGraphWindow.xaml.cs', 'Client/analysis/Windows/EventLogWindow.xaml.cs', 'Client/analysis/Windows/HandleEvidenceWindow.xaml', 'Client/analysis/Windows/HandleEvidenceWindow.xaml.cs', 'Client/analysis/Windows/SimpleEventDetailWindow.xaml', 'Client/analysis/Windows/SimpleEventDetailWindow.xaml.cs', 'Client/analysis/Windows/TelemetryInspectorWindow.xaml', 'Client/analysis/Windows/TelemetryInspectorWindow.xaml.cs' ) }, @{ Message = 'ui/windows: update thread stack views' Paths = @( 'Client/analysis/Windows/ParallelStacksWindow.xaml', 'Client/analysis/Windows/ParallelStacksWindow.xaml.cs', 'Client/analysis/Windows/ThreadStackWindow.xaml', 'Client/analysis/Windows/ThreadStackWindow.xaml.cs' ) }, @{ Message = 'ui/windows: update launch and enrollment flows' Paths = @( 'Client/analysis/Windows/FaultNotificationWindow.cs', 'Client/analysis/Windows/LaunchParametersWindow.xaml', 'Client/analysis/Windows/LaunchParametersWindow.xaml.cs', 'Client/analysis/Windows/LoadingWindow.xaml', 'Client/analysis/Windows/LoadingWindow.xaml.cs', 'Client/analysis/Windows/ProcessPickerWindow.xaml', 'Client/analysis/Windows/ProcessPickerWindow.xaml.cs', 'Client/analysis/Windows/StartupWelcomeWindow.xaml', 'Client/analysis/Windows/StartupWelcomeWindow.xaml.cs', 'Client/analysis/Windows/VmRegistrationWindow.xaml', 'Client/analysis/Windows/VmRegistrationWindow.xaml.cs' ) }, @{ Message = 'ui/windows: refresh floating window hosts' Paths = @( 'Client/analysis/Windows/Floating/EtwFloatWindow.cs', 'Client/analysis/Windows/Floating/EventsFloatWindow.cs', 'Client/analysis/Windows/Floating/HeuristicsFloatWindow.cs', 'Client/analysis/Windows/Floating/MemoryInspectorWindow.cs', 'Client/analysis/Windows/Floating/PerformanceFloatWindow.cs' ) }, @{ Message = 'ui/host: remove legacy operator shell' Paths = @('Client/host') }, @{ Message = 'runner: add headless capture runner' Paths = @('Client/runner') }, @{ Message = 'build/scripts: replace legacy publish scripts' Paths = @( 'Scripts/ci-surface.ps1', 'Scripts/publish-interfaces.ps1', 'Scripts/publish.NET.ps1', 'Scripts/Compile-Win32Resource.ps1' ) }, @{ Message = 'build/scripts: add external rule bundle normalizer' Paths = @('Scripts/Build-YaraBundle.ps1') }, @{ Message = 'build/scripts: add VM registration workflow' Paths = @('Scripts/Register-BlackbirdVm.ps1') }, @{ Message = 'build/scripts: update installer optional components' Paths = @('Scripts/installer.ps1') }, @{ Message = 'build/scripts: update remover optional cleanup' Paths = @('Scripts/remover.ps1') }, @{ Message = 'build/scripts: update Tempest invoke packaging' Paths = @('Scripts/tempest-invoke.ps1') }, @{ Message = 'build/solution: add dllhost and runner projects' Paths = @( 'Blackbird.slnx', 'VCXProj/BlackbirdDllHost.vcxproj', 'VCXProj/BlackbirdDllHost.vcxproj.filters', 'VCXProj/BlackbirdRunner.csproj', 'VCXProj/Blackbird.Runner.manifest', 'VCXProj/Blackbird.Runner.rc' ) }, @{ Message = 'build/kernel: include diagnostics and callback sources' Paths = @('VCXProj/Blackbird.vcxproj', 'VCXProj/Blackbird.vcxproj.filters') }, @{ Message = 'build/controller: update controller project sources' Paths = @('VCXProj/BlackbirdController.vcxproj', 'VCXProj/BlackbirdController.vcxproj.filters') }, @{ Message = 'build/interface: update interface resources' Paths = @('VCXProj/BlackbirdInterface.csproj', 'VCXProj/Blackbird.Interface.manifest', 'VCXProj/Blackbird.Interface.rc') }, @{ Message = 'build/sensor: update sensor and ioctl test projects' Paths = @( 'VCXProj/BlackbirdSensorCore.vcxproj', 'VCXProj/BlackbirdSensorCore.vcxproj.filters', 'VCXProj/BlackbirdIoctlTest.vcxproj', 'VCXProj/BlackbirdIoctlTest.vcxproj.filters' ) }, @{ Message = 'build/usermode: refresh shared props and manifests' Paths = @( 'VCXProj/Blackbird.UserMode.Common.props', 'VCXProj/Blackbird.UserMode.manifest', 'VCXProj/Blackbird.Version.rc', 'VCXProj/Directory.Build.props' ) }, @{ Message = 'build/operator: retire legacy operator projects' Paths = @('VCXProj/BlackbirdOperator.csproj', 'VCXProj/BlackbirdExamples.vcxproj') }, @{ Message = 'build/scripts: add curated public commit helper' Paths = @('Scripts/commit-public-worktree.bat', 'Scripts/Commit-PublicWorktree.ps1') } ) Write-Host "Repository: $repoRoot" Write-Host "Commit groups: $($groups.Count)" if ($PlanOnly) { Write-Host 'Plan-only mode: no git index or commits will be modified.' } else { Write-Host 'Commit mode: commits will be created locally only. This script never pushes.' } foreach ($group in $groups) { Commit-Group -Message $group.Message -Paths $group.Paths } if ($PlanOnly) { exit 0 } $remaining = & git status --short --untracked-files=all $publicRemaining = @() foreach ($line in $remaining) { $normalized = Convert-ToGitPath $line.Substring([Math]::Min(3, $line.Length)) $skip = $false foreach ($skipPath in $privateOrExternalPaths) { $candidate = Convert-ToGitPath $skipPath if ($candidate.EndsWith('/')) { if ($normalized.StartsWith($candidate, [System.StringComparison]::OrdinalIgnoreCase)) { $skip = $true break } } elseif ($normalized.IndexOf($candidate, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) { $skip = $true break } } if (-not $skip) { $publicRemaining += $line } } if ($publicRemaining.Count -gt 0) { Write-Warning 'Some public-looking changes were not included in the curated commit plan:' foreach ($line in $publicRemaining) { Write-Warning " $line" } Write-Warning 'Review these manually before pushing anything.' } Write-Host 'Done. Local commits were created; nothing was pushed.'