ai-config-shield 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dennis Menze
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # AIConfigShield 🛡️
2
+
3
+ ### Stop AI agents from taking shortcuts in your configuration files.
4
+
5
+ ## The Background
6
+ Large Language Models (LLMs) and AI coding agents are powerful, but they have a "laziness" property learned from the vast amount of human data they've been trained on. When tasked with fixing thousands of linting errors or complex architectural issues, they often try to take the path of least resistance.
7
+
8
+ Instead of fixing 1000 errors, an AI might "secretly" modify your configuration files (like `.flake8`, `eslint.config.js`, or `.pre-commit-config.yaml`) to ignore entire categories of rules. You return to your PC after half an hour, the AI proudly reports "All tasks completed!", only for you to find out it actually fixed 10 errors and muted the other 990.
9
+
10
+ Even worse, a simple Windows "Read-Only" attribute is often not enough. Modern AI agents are clever enough to recognize and remove that attribute to continue their shortcut. **AIConfigShield** provides hardened, NTFS-level protection that makes unauthorized modification **impossible** without administrative privileges.
11
+
12
+ ## Features
13
+ - **Unbypassable Locking**: Uses NTFS permissions (`takeown` and `icacls`) to lock files at a system level.
14
+ - **Admin-Only Access**: Prevents any modification by the standard user session. Even "force" writes fail.
15
+ - **Auto-Elevation**: Automatically requests Administrator privileges to apply these deep locks.
16
+ - **Universal**: Use it in any project (Node, Python, Go, etc.) as long as you're on Windows.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install -g ai-config-shield
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Locking your "Fortress"
27
+ Lock your linting configs and hooks to ensure the AI actually *fixes* the code instead of hiding the problems:
28
+
29
+ ```bash
30
+ npx shield-lock eslint.config.js .flake8 .pre-commit-config.yaml .git/hooks/pre-commit
31
+ ```
32
+
33
+ ### Unlocking for Manual Maintenance
34
+ When *you* (the human) want to change the configurations:
35
+
36
+ ```bash
37
+ npx shield-unlock <path-to-file-or-dir>
38
+ ```
39
+
40
+ ## How it works
41
+ The tool performs a "Take Ownership" operation and then modifies the Access Control List (ACL) to grant the current user only **Read & Execute** rights, while preserving **Full Control** for the SYSTEM and Administrators (who must consciously elevate to change these rules).
42
+
43
+ ## License
44
+ MIT
45
+
46
+
package/bin/lock.ps1 ADDED
@@ -0,0 +1,107 @@
1
+ # FILE-LOCK-TOOL: LOCK
2
+ param(
3
+ [Parameter(Mandatory=$true, Position=0)]
4
+ [string[]]$Targets,
5
+ [switch] $SkipProfile
6
+ )
7
+
8
+ $global:LockLogFile = Join-Path (Get-Location).Path "lock_output.log"
9
+
10
+ function Log-Lock {
11
+ param([string]$Msg)
12
+ $timestamp = Get-Date -Format "HH:mm:ss.fff"
13
+ "[$timestamp] $Msg" | Out-File $global:LockLogFile -Append -Encoding utf8
14
+ }
15
+
16
+ Log-Lock "=== LOCK START ==="
17
+ Log-Lock "Args: $($PSBoundParameters | Out-String)"
18
+
19
+ # --- 0. ADMIN-CHECK mit Auto-Elevation ---
20
+ $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
21
+ if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
22
+ $scriptPath = $MyInvocation.MyCommand.Path
23
+ $workDir = (Get-Location).Path
24
+
25
+ $targetArgs = ($Targets | ForEach-Object { "`"$_`"" }) -join " "
26
+ $skipArg = if ($SkipProfile) { " -SkipProfile" } else { "" }
27
+ $cmd = "Set-Location '$workDir'; & '$scriptPath' $targetArgs$skipArg; exit `$LASTEXITCODE"
28
+
29
+ $proc = Start-Process pwsh -ArgumentList "-ExecutionPolicy Bypass", "-Command", $cmd -Verb RunAs -Wait -PassThru
30
+ exit $proc.ExitCode
31
+ }
32
+
33
+ # --- 1. PRE-RESET: Entsperren für Enumeration ---
34
+ Log-Lock "PRE-RESET: Unlocking targets for enumeration..."
35
+ foreach ($targetRaw in $Targets) {
36
+ if (-not (Test-Path $targetRaw)) {
37
+ Log-Lock "WARN: Target not found: $targetRaw"
38
+ continue
39
+ }
40
+ $item = Get-Item $targetRaw -Force -ErrorAction SilentlyContinue
41
+ if ($null -eq $item) { continue }
42
+
43
+ Log-Lock "PRE-RESET: $($item.FullName)"
44
+ if ($item.PSIsContainer) {
45
+ & icacls "$($item.FullName)" /reset /T /C 2>&1 | Out-Null
46
+ } else {
47
+ & icacls "$($item.FullName)" /reset /C 2>&1 | Out-Null
48
+ }
49
+ }
50
+ Log-Lock "PRE-RESET: Done"
51
+
52
+ # --- 2. SCHUTZ ANWENDEN ---
53
+ $allFiles = @()
54
+ foreach ($targetRaw in $Targets) {
55
+ if (-not (Test-Path $targetRaw)) { continue }
56
+ $item = Get-Item $targetRaw -Force -ErrorAction SilentlyContinue
57
+ if ($null -eq $item) { continue }
58
+
59
+ if ($item -is [System.IO.DirectoryInfo]) {
60
+ $allFiles += $item.FullName
61
+ try {
62
+ $children = Get-ChildItem $item.FullName -Recurse -Force -ErrorAction Stop
63
+ $allFiles += ($children | ForEach-Object { $_.FullName })
64
+ } catch {
65
+ Log-Lock "WARN: Cannot enumerate $($item.FullName): $_"
66
+ }
67
+ } else {
68
+ $allFiles += $item.FullName
69
+ }
70
+ }
71
+
72
+ Log-Lock "Expanded targets to $($allFiles.Count) files"
73
+
74
+ foreach ($fullPath in $allFiles) {
75
+ if (-not (Test-Path $fullPath)) { continue }
76
+ $item = Get-Item $fullPath -Force
77
+ $isDir = ($item -is [System.IO.DirectoryInfo])
78
+
79
+ Log-Lock "--- Processing: $fullPath (Dir=$isDir) ---"
80
+ Write-Host "🛡️ Sperre $(Split-Path $fullPath -Leaf)..." -ForegroundColor Cyan
81
+
82
+ # A. Besitz erzwingen (takeown)
83
+ Log-Lock "STEP: takeown"
84
+ $out = & takeown /F "$fullPath" /A 2>&1 | Out-String
85
+ Log-Lock "OUTPUT: $out"
86
+
87
+ # B. Vererbung kappen und aufräumen
88
+ Log-Lock "STEP: icacls reset"
89
+ $out = & icacls "$fullPath" /reset /c 2>&1 | Out-String
90
+ Log-Lock "OUTPUT: $out"
91
+
92
+ Log-Lock "STEP: icacls inheritance:r"
93
+ $out = & icacls "$fullPath" /inheritance:r /c 2>&1 | Out-String
94
+ Log-Lock "OUTPUT: $out"
95
+
96
+ # C. Lesezugriff erlauben
97
+ Log-Lock "STEP: icacls grant RX"
98
+ $out = & icacls "$fullPath" /grant "${env:USERNAME}:(RX)" /c 2>&1 | Out-String
99
+ Log-Lock "OUTPUT: $out"
100
+
101
+ Log-Lock "STEP: icacls grant Admin+System"
102
+ $out = & icacls "$fullPath" /grant "*S-1-5-32-544:(F)" /grant "SYSTEM:(F)" /c 2>&1 | Out-String
103
+ Log-Lock "OUTPUT: $out"
104
+
105
+ Log-Lock "--- DONE: $fullPath ---"
106
+ Write-Host "✅ GESPERRT: $(Split-Path $fullPath -Leaf)" -ForegroundColor Green
107
+ }
package/bin/unlock.ps1 ADDED
@@ -0,0 +1,79 @@
1
+ # FILE-LOCK-TOOL: UNLOCK
2
+ param(
3
+ [Parameter(Mandatory=$true, Position=0)]
4
+ [string[]]$Targets,
5
+ [switch] $SkipProfile
6
+ )
7
+
8
+ $global:UnlockLogFile = Join-Path (Get-Location).Path "unlock_output.log"
9
+
10
+ function Log-Unlock {
11
+ param([string]$Msg)
12
+ $timestamp = Get-Date -Format "HH:mm:ss.fff"
13
+ "[$timestamp] $Msg" | Out-File $global:UnlockLogFile -Append -Encoding utf8
14
+ }
15
+
16
+ Log-Unlock "=== UNLOCK START ==="
17
+
18
+ # --- 0. ADMIN-CHECK mit Auto-Elevation ---
19
+ $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
20
+ if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
21
+ $scriptPath = $MyInvocation.MyCommand.Path
22
+ $workDir = (Get-Location).Path
23
+
24
+ $targetArgs = ($Targets | ForEach-Object { "`"$_`"" }) -join " "
25
+ $skipArg = if ($SkipProfile) { " -SkipProfile" } else { "" }
26
+ $cmd = "Set-Location '$workDir'; & '$scriptPath' $targetArgs$skipArg; exit `$LASTEXITCODE"
27
+
28
+ $proc = Start-Process pwsh -ArgumentList "-ExecutionPolicy Bypass", "-Command", $cmd -Verb RunAs -Wait -PassThru
29
+ exit $proc.ExitCode
30
+ }
31
+
32
+ # --- 1. ENTSPERRUNG ---
33
+ foreach ($targetRaw in $Targets) {
34
+ if (-not (Test-Path $targetRaw)) {
35
+ Log-Unlock "WARN: Target not found: $targetRaw"
36
+ continue
37
+ }
38
+ $item = Get-Item $targetRaw -Force
39
+ $fullPath = $item.FullName
40
+ $isDir = $item.PSIsContainer
41
+ $fileName = Split-Path $fullPath -Leaf
42
+
43
+ Log-Unlock "--- Processing: $fullPath (Dir=$isDir) ---"
44
+ Write-Host "🔓 Entsperre $fileName (Dir=$isDir)..." -ForegroundColor Cyan
45
+
46
+ $takeownArgs = @("/F", "$fullPath", "/A")
47
+ if ($isDir) { $takeownArgs += "/R"; $takeownArgs += "/D"; $takeownArgs += "Y" }
48
+
49
+ # A. Besitz erzwingen (Admin)
50
+ Log-Unlock "STEP: takeown"
51
+ $out = & takeown $takeownArgs 2>&1 | Out-String
52
+ Log-Unlock "OUTPUT: $out"
53
+
54
+ # B. Integrität zurück auf Medium setzen
55
+ $intArgs = @("$fullPath", "/setintegritylevel", "M", "/c")
56
+ if ($isDir) { $intArgs += "/T" }
57
+ Log-Unlock "STEP: setintegritylevel M"
58
+ $out = & icacls $intArgs 2>&1 | Out-String
59
+ Log-Unlock "OUTPUT: $out"
60
+
61
+ # C. ACLs komplett zurücksetzen
62
+ $resetArgs = @("$fullPath", "/reset", "/c")
63
+ if ($isDir) { $resetArgs += "/T" }
64
+ Log-Unlock "STEP: reset"
65
+ $out = & icacls $resetArgs 2>&1 | Out-String
66
+ Log-Unlock "OUTPUT: $out"
67
+
68
+ # D. Besitzer zurück an User
69
+ $ownArgs = @("$fullPath", "/setowner", "${env:USERNAME}", "/c")
70
+ if ($isDir) { $ownArgs += "/T" }
71
+ Log-Unlock "STEP: setowner ${env:USERNAME}"
72
+ $out = & icacls $ownArgs 2>&1 | Out-String
73
+ Log-Unlock "OUTPUT: $out"
74
+
75
+ Log-Unlock "--- DONE: $fullPath ---"
76
+ Write-Host "✅ $fileName (und Inhalte) entsperrt." -ForegroundColor Green
77
+ }
78
+
79
+ Log-Unlock "=== UNLOCK END ==="
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "ai-config-shield",
3
+ "version": "1.0.0",
4
+ "description": "Protect your configuration files from being silently bypassed by AI agents.",
5
+ "bin": {
6
+ "shield-lock": "./bin/lock.ps1",
7
+ "shield-unlock": "./bin/unlock.ps1"
8
+ },
9
+ "scripts": {
10
+ "lock": "pwsh -ExecutionPolicy Bypass -File ./bin/lock.ps1",
11
+ "unlock": "pwsh -ExecutionPolicy Bypass -File ./bin/unlock.ps1"
12
+ },
13
+ "keywords": [
14
+ "ai",
15
+ "shield",
16
+ "lock",
17
+ "unlock",
18
+ "permissions",
19
+ "security",
20
+ "windows",
21
+ "powershell",
22
+ "lint",
23
+ "config"
24
+ ],
25
+ "author": "Denksystem",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/denksystem/AIConfigShield.git"
30
+ },
31
+ "os": [
32
+ "win32"
33
+ ]
34
+ }
@@ -0,0 +1,38 @@
1
+ param(
2
+ [string[]] $Files = @(
3
+ "package.json",
4
+ "pyproject.toml",
5
+ ".husky\pre-commit",
6
+ ".husky\pre-push"
7
+ )
8
+ )
9
+
10
+ $allLocked = $true
11
+ $allOpen = $true
12
+
13
+ foreach ($f in $Files) {
14
+ if (Test-Path $f) {
15
+ try {
16
+ # Versuch, die Datei zu öffnen (Lesen+Schreiben)
17
+ $stream = [System.IO.File]::Open(
18
+ $f,
19
+ [System.IO.FileMode]::Open,
20
+ [System.IO.FileAccess]::ReadWrite
21
+ )
22
+ $stream.Close()
23
+ Write-Host "OPEN: $f" -ForegroundColor Green
24
+ $allLocked = $false
25
+ } catch {
26
+ Write-Host "LOCKED: $f" -ForegroundColor Red
27
+ $allOpen = $false
28
+ }
29
+ }
30
+ }
31
+
32
+ if ($allLocked) {
33
+ exit 10
34
+ } # Code 10 = ALL LOCKED
35
+ if ($allOpen) {
36
+ exit 20
37
+ } # Code 20 = ALL OPEN
38
+ exit 30 # Code 30 = MIXED
@@ -0,0 +1,220 @@
1
+ # MASTER E2E SECURITY & ANTI-GHOST AUDIT (Fail-Fast)
2
+ param(
3
+ [switch] $NoElevate,
4
+ [switch] $VerifyOnly,
5
+ [string] $Expect = "All" # All, Locked, Open
6
+ )
7
+ Write-Host "DEBUG: E2E SCRIPT STARTED (Param Expect=$Expect, VerifyOnly=$VerifyOnly, NoElevate=$NoElevate)"
8
+
9
+ # --- 0. ADMIN-CHECK ---
10
+ $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
11
+ if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -and -not $NoElevate) {
12
+ Write-Host "CRITICAL: E2E-Test erfordert Administrator-Rechte!" -ForegroundColor Red
13
+ Start-Process pwsh -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
14
+ exit
15
+ }
16
+
17
+ $baseFiles = @("package.json", "pyproject.toml", ".husky")
18
+
19
+ # Log File Setup
20
+ $global:AuditLogFile = "$PSScriptRoot\audit_debug.log"
21
+ "--- NEW AUDIT RUN $(Get-Date) ---" | Out-File $global:AuditLogFile -Append -Encoding utf8
22
+
23
+ function Log-Audit {
24
+ param([string]$Msg)
25
+ $timestamp = Get-Date -Format "HH:mm:ss.fff"
26
+ "[$timestamp] $Msg" | Out-File $global:AuditLogFile -Append -Encoding utf8
27
+ # Auch in Konsole für Live-Log
28
+ Microsoft.PowerShell.Utility\Write-Host "[$timestamp] $Msg" -ForegroundColor Gray
29
+ }
30
+
31
+ # 1. Alle Dateien auflösen (rekursiv bei Ordnern wie .husky)
32
+ $files = @()
33
+ foreach ($f in $baseFiles) {
34
+ if (Test-Path $f) {
35
+ $item = Get-Item $f
36
+ if ($item -is [System.IO.DirectoryInfo]) {
37
+ $files += Get-ChildItem $f -File -Recurse | ForEach-Object { $_.FullName }
38
+ } else {
39
+ $files += $item.FullName
40
+ }
41
+ }
42
+ }
43
+
44
+ # --- 1. STEP RUNNER (STRICT) ---
45
+ function Run-Step {
46
+ param(
47
+ [string]$ActionName,
48
+ [scriptblock]$Action,
49
+ [switch]$ExpectedUnlocked,
50
+ [string]$TargetFile
51
+ )
52
+
53
+ Log-Audit "START: $ActionName on $TargetFile (Unlocked=$ExpectedUnlocked)"
54
+
55
+ # State Reset
56
+ $error.Clear()
57
+ $global:lastIdManualOverride = [int](Get-Random)
58
+ $global:lastSeenErrorCount = 0
59
+
60
+ $contentBefore = if ($TargetFile -and (Test-Path $TargetFile)) {
61
+ try { Get-Content $TargetFile -Raw -ErrorAction SilentlyContinue } catch { "" }
62
+ } else { "" }
63
+ # Reset State
64
+ $script:shadowWarningShown = $false
65
+ $global:AuditTargetFile = $TargetFile
66
+ $global:AuditWarningTriggered = $false
67
+ $global:SimulatedSecurityError = $null
68
+ $global:AuditLastExitCode = $null
69
+ $readErrorBefore = $false
70
+ $contentBefore = if ($TargetFile -and (Test-Path $TargetFile)) {
71
+ try { Get-Content $TargetFile -Raw -ErrorAction Stop } catch { $readErrorBefore = $true; Log-Audit "READ ERROR BEFORE: $_"; "" }
72
+ } else { "" }
73
+
74
+ $failed = $false
75
+ try {
76
+ $ErrorActionPreference = "Continue" # Continue damit Stderr captured werden kann
77
+
78
+ # Capture Output (Stdout + Stderr merged via 2>&1)
79
+ # Wir wollen es im Log UND (optional) sehen? Nein, primär Log.
80
+ $cmdOutput = & $Action 2>&1 | Out-String
81
+
82
+ # Logge den KOMPLETTEN Output
83
+ Log-Audit "OUTPUT from '$ActionName':"
84
+ if ($cmdOutput) {
85
+ $cmdOutput -split "`n" | ForEach-Object { Log-Audit " > $_" }
86
+ } else {
87
+ Log-Audit " (No Output)"
88
+ }
89
+
90
+ $global:AuditLastExitCode = $LASTEXITCODE
91
+ Log-Audit "Action finished. ExitCode: $LASTEXITCODE"
92
+
93
+ if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne $null) {
94
+ $failed = $true
95
+ if (-not $global:SimulatedSecurityError) {
96
+ $global:SimulatedSecurityError = [PSCustomObject]@{
97
+ Exception = [PSCustomObject]@{ Message = "Native Command Failure for $TargetFile (ExitCode $LASTEXITCODE)" }
98
+ }
99
+ }
100
+ }
101
+ } catch {
102
+ $failed = $true
103
+ # Sicherstellen, dass der Fehler in $global:Error landet für den Hook
104
+ $global:SimulatedSecurityError = $_
105
+ Log-Audit "CATCH: $($_.Exception.Message)"
106
+ }
107
+
108
+ # Fallback: Wenn Failed aber kein Fehlerobjekt (z.B. Native Git Failure)
109
+ # Und sicherstellen, dass wir es wirklich setzen!
110
+ if ($failed -and -not $global:SimulatedSecurityError) {
111
+ $global:SimulatedSecurityError = [PSCustomObject]@{
112
+ Exception = [PSCustomObject]@{ Message = "Generischer Fehler für $TargetFile (ExitCode $global:AuditLastExitCode)" }
113
+ }
114
+ }
115
+
116
+ # Trigger Hook (explicitly invoke prompt with current state)
117
+ if (Get-Command prompt -ErrorAction SilentlyContinue) {
118
+ prompt | Out-Null
119
+ }
120
+
121
+ $warningTriggered = $global:AuditWarningTriggered
122
+ $readErrorAfter = $false
123
+ $contentAfter = if ($TargetFile -and (Test-Path $TargetFile)) {
124
+ try { Get-Content $TargetFile -Raw -ErrorAction Stop } catch { $readErrorAfter = $true; Log-Audit "READ ERROR AFTER: $_"; "" }
125
+ } else { "" }
126
+
127
+ # Modified Logic: Only if NO Read Errors occurred
128
+ if ($readErrorBefore -or $readErrorAfter) {
129
+ $wasActuallyModified = $false
130
+ Log-Audit "SKIPPING Modified Check due to Read Errors (Before=$readErrorBefore, After=$readErrorAfter)"
131
+ } else {
132
+ $wasActuallyModified = ($contentBefore -ne $contentAfter)
133
+ }
134
+
135
+ $errorDetail = if ($global:SimulatedSecurityError) { $global:SimulatedSecurityError.Exception.Message } else { "Keine Details (Failed=$failed)" }
136
+
137
+ if ($ExpectedUnlocked) {
138
+ if ($wasActuallyModified -or -not $failed) {
139
+ Log-Audit "SUCCESS: Allowed ($ActionName)"
140
+ return $true
141
+ } else {
142
+ Log-Audit "FAIL: Unexpected Block ($ActionName) - $errorDetail"
143
+ Microsoft.PowerShell.Utility\Write-Host " ❌ FEHLER: Unerwartet blockiert ($TargetFile)! Fehler: $errorDetail" -ForegroundColor Red
144
+ return $false
145
+ }
146
+ } else {
147
+ if ($wasActuallyModified) {
148
+ Log-Audit "CRITICAL: File Modified ($ActionName)"
149
+ Log-Audit " Before Len: $($contentBefore.Length)"
150
+ Log-Audit " After Len: $($contentAfter.Length)"
151
+ if ($contentBefore -eq $null) { Log-Audit " Before is NULL" }
152
+ if ($contentAfter -eq $null) { Log-Audit " After is NULL" }
153
+
154
+ Microsoft.PowerShell.Utility\Write-Host " ❌ SICHERHEITSLÜCKE: Datei $TargetFile manipuliert!" -ForegroundColor White -BackgroundColor Red
155
+ return $false
156
+ }
157
+
158
+ if (-not $warningTriggered) {
159
+ Log-Audit "FAIL-FAST: No Warning ($ActionName)"
160
+ Log-Audit " ErrDetails: $errorDetail"
161
+ Log-Audit " Pattern: $(Split-Path $TargetFile -Leaf)"
162
+
163
+ Microsoft.PowerShell.Utility\Write-Host " ❌ FAIL-FAST: Keine Warnung für $TargetFile" -ForegroundColor Red
164
+ return $false
165
+ }
166
+ Log-Audit "SUCCESS: Blocked + Warned ($ActionName)"
167
+ return $true
168
+ }
169
+ }
170
+
171
+ # --- 2. AUDIT EXECUTION ---
172
+ try {
173
+ # 1. Dateiliste bereinigen (Duplikate entfernen)
174
+ $files = $files | Select-Object -Unique
175
+
176
+ if (Test-Path "./scripts/warning-hook.ps1") {
177
+ # Wichtig: Pattern muss auf LEAF-Namen basieren für Shell-Kompatibilität
178
+ $patternList = $files | ForEach-Object { Split-Path $_ -Leaf | ForEach-Object { [Regex]::Escape($_) } }
179
+ $global:ProtectedFilesPattern = ($patternList | Select-Object -Unique) -join '|'
180
+ . ./scripts/warning-hook.ps1
181
+ }
182
+
183
+ if (-not $VerifyOnly) {
184
+ Microsoft.PowerShell.Utility\Write-Host ">>> PHASE 1: LOCKING <<<" -ForegroundColor Yellow
185
+ . ./scripts/lock-files.ps1 -SkipProfile | Out-Null
186
+ }
187
+
188
+ if ($Expect -eq "All" -or $Expect -eq "Locked") {
189
+ foreach ($f in $files) {
190
+ if (Test-Path $f) {
191
+ # ACL Debug
192
+ try {
193
+ $acl = Get-Acl $f
194
+ Log-Audit "DEBUG ACL $f :: Owner=$($acl.Owner) Access=$($acl.AccessToString)"
195
+ } catch { Log-Audit "DEBUG ACL FAIL: $_" }
196
+
197
+ if (-not (Run-Step "Write Check" { "hack" | Out-File $f -Force } -TargetFile $f)) { exit 1 }
198
+ if (-not (Run-Step "Delete Check" { Remove-Item $f -Force } -TargetFile $f)) { exit 1 }
199
+
200
+ # Restore Check kann tricky sein bei untracked files, aber wir testen nur tracked files
201
+ if (-not (Run-Step "Restore Check" { git restore -s HEAD $f 2>&1 | Out-Null } -TargetFile $f)) { exit 1 }
202
+ }
203
+ }
204
+ }
205
+
206
+ if ($Expect -eq "All" -or $Expect -eq "Open") {
207
+ foreach ($f in $files) {
208
+ if (Test-Path $f) {
209
+ # Read Check: Versuche Inhalt zu lesen statt git add (weniger Side-Effects)
210
+ if (-not (Run-Step "Read Check" { Get-Content $f | Select-Object -First 1 } -ExpectedUnlocked -TargetFile $f)) { exit 1 }
211
+ }
212
+ }
213
+ }
214
+
215
+ Microsoft.PowerShell.Utility\Write-Host "`nAUDIT BESTANDEN! ✅" -ForegroundColor Green
216
+ exit 0
217
+ } finally {
218
+ if (-not $VerifyOnly) { . ./scripts/unlock-files.ps1 -Targets $baseFiles | Out-Null }
219
+ }
220
+ exit 0
@@ -0,0 +1,120 @@
1
+ # FINAL UAC DEMO (FULL CONSENT PROOF)
2
+ # Zeigt, dass KEINE Aenderung ohne explizite Zustimmung moeglich ist.
3
+ # Ablauf:
4
+ # 1. LOCK -> JA (Erfolg)
5
+ # 2. UNLOCK -> NEIN (Muss fehlschlagen, Datei bleibt gesperrt)
6
+ # 3. UNLOCK -> JA (Erfolg)
7
+ # 4. LOCK -> NEIN (Muss fehlschlagen, Datei bleibt offen)
8
+
9
+ $pwd = (Get-Item .).FullName
10
+
11
+ # Helper for detailed logging
12
+ function Log-Demo {
13
+ param([string]$Msg)
14
+ $timestamp = Get-Date -Format "HH:mm:ss.fff"
15
+ $logFile = Join-Path $pwd "scripts/audit_debug.log"
16
+ "[$timestamp] [DEMO-CONTROLLER] $Msg" | Out-File $logFile -Append -Encoding utf8
17
+ Write-Host $Msg -ForegroundColor DarkGray
18
+ }
19
+
20
+ # Clear all logs at start
21
+ $scriptsDir = Join-Path $pwd "scripts"
22
+ Remove-Item (Join-Path $scriptsDir "audit_debug.log") -ErrorAction SilentlyContinue
23
+ Remove-Item (Join-Path $scriptsDir "lock_output.log") -ErrorAction SilentlyContinue
24
+ Remove-Item (Join-Path $scriptsDir "unlock_output.log") -ErrorAction SilentlyContinue
25
+ Remove-Item (Join-Path $scriptsDir "lock_processing.log") -ErrorAction SilentlyContinue
26
+
27
+ Log-Demo "--- DEMO STARTED ---"
28
+
29
+ function Show-Header {
30
+ param($Text)
31
+ Log-Demo "PHASE START: $Text"
32
+ Write-Host "`n====================================================" -ForegroundColor Magenta
33
+ Write-Host " $Text" -ForegroundColor Magenta
34
+ Write-Host "====================================================" -ForegroundColor Magenta
35
+ }
36
+
37
+ function Run-Audit {
38
+ param($TargetExpect)
39
+ Log-Demo "Run-Audit Expect=$TargetExpect Start"
40
+ # Wir rufen das Audit-Skript auf und geben nur relevante Zeilen aus
41
+ # -NoElevate wird genutzt, da die Demo bereits UAC/Admin-Kontext steuert.
42
+
43
+ $auditPath = Join-Path $pwd "scripts/e2e-security-audit.ps1"
44
+
45
+ # Starte Audit als NEUEN Prozess, damit Windows frische ACLs liest (kein Caching vom Parent)
46
+ $proc = Start-Process pwsh -ArgumentList "-ExecutionPolicy Bypass", "-File `"$auditPath`"", "-Expect $TargetExpect", "-VerifyOnly", "-NoElevate" -Wait -PassThru -NoNewWindow
47
+ $auditExitCode = $proc.ExitCode
48
+
49
+ if ($auditExitCode -ne 0) {
50
+ Log-Demo "Run-Audit Failed with $auditExitCode"
51
+ Write-Host "🚨 DEMO ABBRUCH: Audit fehlgeschlagen mit Exit-Code: $auditExitCode" -ForegroundColor Red
52
+ if (Test-Path "$PSScriptRoot\audit_debug.log") {
53
+ Write-Host "`n--- AUDIT LOG (FEHLER DETAILS) ---" -ForegroundColor Yellow
54
+ Get-Content "$PSScriptRoot\audit_debug.log" -Tail 30
55
+ Write-Host "----------------------------------" -ForegroundColor Yellow
56
+ }
57
+ # NICHT automatisch entsperren - User soll manuell unlock-files.ps1 ausführen wenn nötig
58
+ exit 1
59
+ }
60
+ Log-Demo "Run-Audit Success"
61
+ Write-Host "✨ Audit ($TargetExpect) bestanden!" -ForegroundColor Green
62
+ }
63
+
64
+ # --- PHASE 1: LOCK (JA) ---
65
+ Show-Header "PHASE 1: SPERRE AKTIVIEREN (KLICK: JA)"
66
+ Write-Host ">>> BITTE JETZT IM WINDOWS-FENSTER AUF 'JA' KLICKEN <<<" -ForegroundColor Red -BackgroundColor White
67
+
68
+ Log-Demo "Sending UAC Request for lock-files.ps1..."
69
+ Start-Process pwsh -ArgumentList "-NoProfile", "-File", "$pwd/scripts/lock-files.ps1", "-SkipProfile" -Verb RunAs -Wait
70
+ Log-Demo "UAC Request finished."
71
+
72
+ Write-Host "`nAUDIT 1: Prüfe ob Dateien GESPERRT sind (und Warnung kommt!)..." -ForegroundColor Yellow
73
+ Run-Audit "Locked"
74
+
75
+
76
+ # --- PHASE 2: UNLOCK (NEIN) ---
77
+ Show-Header "PHASE 2: ENTSPERREN ABLEHNEN (KLICK: NEIN)"
78
+ Write-Host "Wir versuchen den Schutz aufzuheben. Du verbietest es." -ForegroundColor Gray
79
+ Write-Host ">>> BITTE JETZT IM WINDOWS-FENSTER AUF 'NEIN' / 'ABBRECHEN' KLICKEN <<<" -ForegroundColor Red -BackgroundColor White
80
+
81
+ try {
82
+ Start-Process pwsh -ArgumentList "-NoProfile", "-File", "$pwd/scripts/unlock-files.ps1" -Verb RunAs -Wait -ErrorAction Stop
83
+ Write-Host "`nHinweis: Du hast 'JA' geklickt, wir wollten 'NEIN' testen." -ForegroundColor Gray
84
+ } catch {
85
+ Write-Host "`n✅ ERFOLG: Du hast das Entsperren verweigert!" -ForegroundColor Green
86
+ }
87
+
88
+ Write-Host "`nAUDIT 2: Prüfe ob Dateien IMMER NOCH GESPERRT sind..." -ForegroundColor Yellow
89
+ Run-Audit "Locked"
90
+
91
+
92
+ # --- PHASE 3: UNLOCK (JA) ---
93
+ Show-Header "PHASE 3: ENTSPERREN ERLAUBEN (KLICK: JA)"
94
+ Write-Host "Jetzt erlauben wir es wirklich." -ForegroundColor Gray
95
+ Write-Host ">>> BITTE JETZT IM WINDOWS-FENSTER AUF 'JA' KLICKEN <<<" -ForegroundColor Red -BackgroundColor White
96
+
97
+ Start-Process pwsh -ArgumentList "-NoProfile", "-File", "$pwd/scripts/unlock-files.ps1" -Verb RunAs -Wait
98
+
99
+ Write-Host "`nAUDIT 3: Prüfe ob Dateien jetzt OFFEN sind..." -ForegroundColor Yellow
100
+ Run-Audit "Open"
101
+
102
+
103
+ # --- PHASE 4: LOCK (NEIN) ---
104
+ Show-Header "PHASE 4: SPERRE ABLEHNEN (KLICK: NEIN)"
105
+ Write-Host "Versuch einer erneuten Sperre. Du verbietest es." -ForegroundColor Gray
106
+ Write-Host ">>> BITTE JETZT IM WINDOWS-FENSTER AUF 'NEIN' / 'ABBRECHEN' KLICKEN <<<" -ForegroundColor Red -BackgroundColor White
107
+
108
+ try {
109
+ Start-Process pwsh -ArgumentList "-NoProfile", "-File", "$pwd/scripts/lock-files.ps1", "-SkipProfile" -Verb RunAs -Wait -ErrorAction Stop
110
+ Write-Host "`nHinweis: Du hast 'JA' geklickt, wir wollten 'NEIN' testen." -ForegroundColor Gray
111
+ } catch {
112
+ Write-Host "`n✅ ERFOLG: Du hast das Sperren verweigert!" -ForegroundColor Green
113
+ }
114
+
115
+ Write-Host "`nAUDIT 4: Prüfe ob Dateien IMMER NOCH OFFEN sind..." -ForegroundColor Yellow
116
+ Run-Audit "Open"
117
+
118
+
119
+ Show-Header "DEMO BEENDET: EXAKT 4 ADMIN-ENTSCHEIDUNGEN (JA, NEIN, JA, NEIN)"
120
+ Write-Host "Beweisführung abgeschlossen: Ohne dein JA passiert hier gar nichts." -ForegroundColor Green