-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path11_MixinScriptblock.ps1
More file actions
129 lines (114 loc) · 4.47 KB
/
11_MixinScriptblock.ps1
File metadata and controls
129 lines (114 loc) · 4.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<#
.SYNOPSIS
OOP Reference: Mixin / Scriptblock Injection
.DESCRIPTION
Topic: Inject cross-cutting behavior via scriptblocks without subclassing
Category: Structural
Agent Task: Add a timing mixin that wraps Encrypt() and logs elapsed milliseconds
to a [System.Collections.Generic.List[hashtable]] on the object.
Add Pester tests that verify the hook fires and elapsed > 0.
Done Conditions:
- Hook fires before encrypt
- Timing mixin records elapsed without modifying MixableService
- Pester tests pass: Invoke-Pester -Output Detailed
Non-Scope:
- No async hooks
#>
function Add-TimingMixin {
param(
[object]$Target,
[string[]]$MethodNames
)
foreach ($methodName in $MethodNames) {
$timedScript = {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
try {
$result = $this.$methodName.Invoke($args)
return $result
}
finally {
$sw.Stop()
Write-Verbose "[$methodName] $($sw.ElapsedMilliseconds)ms"
}
}.GetNewClosure()
$Target | Add-Member -MemberType ScriptMethod -Name "${methodName}Timed" -Value $timedScript -Force
}
}
class MixableService {
[scriptblock]$OnEncrypt = {}
[scriptblock]$OnDecrypt = {}
hidden [byte[]]$_key
MixableService() {
$this._key = [byte[]]::new(32)
[System.Security.Cryptography.RandomNumberGenerator]::Fill($this._key)
}
[byte[]] Encrypt([byte[]]$data) {
& $this.OnEncrypt $data
$gcm = [System.Security.Cryptography.AesGcm]::new($this._key)
$nonce = [byte[]]::new(12); $ct = [byte[]]::new($data.Length); $tag = [byte[]]::new(16)
[System.Security.Cryptography.RandomNumberGenerator]::Fill($nonce)
$gcm.Encrypt($nonce, $data, $ct, $tag); $gcm.Dispose()
return $nonce + $tag + $ct
}
}
# Inject audit hook:
# $svc = [MixableService]::new()
# $svc.OnEncrypt = { param($d); Write-Host "[AUDIT] $($d.Length) bytes at $(Get-Date -Format u)" }
# $svc.Encrypt([System.Text.Encoding]::UTF8.GetBytes("test"))
# ================================================================================
# Agent Task: Timing Mixin Implementation
# ================================================================================
<#
.SYNOPSIS
Add timing mixin that wraps Encrypt() and logs elapsed milliseconds
.DESCRIPTION
Demonstrates scriptblock injection for cross-cutting timing behavior:
- Hook fires before encrypt operation (via OnEncrypt scriptblock)
- Records elapsed milliseconds to a List<hashtable> on the object
- Does not modify MixableService class definition
- Uses Add-Member to inject TimingLog property and wrapped method
#>
function Add-EncryptTimingMixin {
<#
.SYNOPSIS
Inject timing behavior into MixableService.Encrypt() method
.PARAMETER Target
The MixableService instance to enhance with timing
.EXAMPLE
$svc = [MixableService]::new()
Add-EncryptTimingMixin -Target $svc
$svc.EncryptTimed([System.Text.Encoding]::UTF8.GetBytes("test"))
$svc.TimingLog # Shows elapsed milliseconds
#>
param(
[Parameter(Mandatory)]
[MixableService]$Target
)
# Add TimingLog property if not already present
if (-not ($Target.PSObject.Properties.Name -contains 'TimingLog')) {
$timingLog = [System.Collections.Generic.List[hashtable]]::new()
$Target | Add-Member -MemberType NoteProperty -Name 'TimingLog' -Value $timingLog -Force
}
# Add wrapped EncryptTimed method using scriptblock
# This method handles both timing AND firing the OnEncrypt hook
$timedEncryptScript = {
param([byte[]]$data)
# Start timing
$sw = [System.Diagnostics.Stopwatch]::StartNew()
# Call original Encrypt (which will fire the OnEncrypt hook)
$result = $this.Encrypt($data)
# Stop timing and record to log
$sw.Stop()
$entry = @{
Timestamp = [DateTime]::UtcNow
Method = 'Encrypt'
ElapsedMs = $sw.ElapsedMilliseconds
DataSize = $data.Length
}
if ($this.PSObject.Properties.Name -contains 'TimingLog') {
$this.TimingLog.Add($entry)
}
return $result
}
$Target | Add-Member -MemberType ScriptMethod -Name 'EncryptTimed' -Value $timedEncryptScript -Force
}