Class: Aidp::Security::TrifectaState

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/security/trifecta_state.rb

Overview

Tracks the three security flags that form the "lethal trifecta" Per Rule of Two: never enable all three simultaneously

Flags:

  • untrusted_input: Processing content from untrusted sources (issues, PRs, external data)
  • private_data: Access to secrets, credentials, or sensitive data
  • egress: Ability to communicate externally (git push, API calls, network access)

State is tracked per work unit and resets between units.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(work_unit_id: nil) ⇒ TrifectaState

Returns a new instance of TrifectaState.



20
21
22
23
24
25
26
27
28
29
# File 'lib/aidp/security/trifecta_state.rb', line 20

def initialize(work_unit_id: nil)
  @work_unit_id = work_unit_id || SecureRandom.hex(8)
  @untrusted_input = false
  @private_data = false
  @egress = false
  @untrusted_input_source = nil
  @private_data_source = nil
  @egress_source = nil
  @frozen = false
end

Instance Attribute Details

#egressObject (readonly)

Returns the value of attribute egress.



15
16
17
# File 'lib/aidp/security/trifecta_state.rb', line 15

def egress
  @egress
end

#egress_sourceObject (readonly)

Track sources for audit logging



18
19
20
# File 'lib/aidp/security/trifecta_state.rb', line 18

def egress_source
  @egress_source
end

#private_dataObject (readonly)

Returns the value of attribute private_data.



15
16
17
# File 'lib/aidp/security/trifecta_state.rb', line 15

def private_data
  @private_data
end

#private_data_sourceObject (readonly)

Track sources for audit logging



18
19
20
# File 'lib/aidp/security/trifecta_state.rb', line 18

def private_data_source
  @private_data_source
end

#untrusted_inputObject (readonly)

Returns the value of attribute untrusted_input.



15
16
17
# File 'lib/aidp/security/trifecta_state.rb', line 15

def untrusted_input
  @untrusted_input
end

#untrusted_input_sourceObject (readonly)

Track sources for audit logging



18
19
20
# File 'lib/aidp/security/trifecta_state.rb', line 18

def untrusted_input_source
  @untrusted_input_source
end

#work_unit_idObject (readonly)

Returns the value of attribute work_unit_id.



15
16
17
# File 'lib/aidp/security/trifecta_state.rb', line 15

def work_unit_id
  @work_unit_id
end

Instance Method Details

#disable(flag) ⇒ TrifectaState

Disable a flag

Parameters:

  • :untrusted_input, :private_data, or :egress

Returns:

  • self for chaining

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/aidp/security/trifecta_state.rb', line 97

def disable(flag)
  raise FrozenStateError, "Cannot modify frozen trifecta state" if @frozen

  case flag
  when :untrusted_input
    @untrusted_input = false
    @untrusted_input_source = nil
  when :private_data
    @private_data = false
    @private_data_source = nil
  when :egress
    @egress = false
    @egress_source = nil
  else
    raise ArgumentError, "Unknown trifecta flag: #{flag}"
  end

  log_flag_disabled(flag)
  self
end

#dupObject

Create a copy of this state (unfrozen)



131
132
133
134
135
136
137
138
139
140
# File 'lib/aidp/security/trifecta_state.rb', line 131

def dup
  new_state = TrifectaState.new(work_unit_id: "#{@work_unit_id}_dup")
  new_state.instance_variable_set(:@untrusted_input, @untrusted_input)
  new_state.instance_variable_set(:@private_data, @private_data)
  new_state.instance_variable_set(:@egress, @egress)
  new_state.instance_variable_set(:@untrusted_input_source, @untrusted_input_source)
  new_state.instance_variable_set(:@private_data_source, @private_data_source)
  new_state.instance_variable_set(:@egress_source, @egress_source)
  new_state
end

#enable(flag, source: nil) ⇒ TrifectaState

Enable a flag with source tracking

Parameters:

  • :untrusted_input, :private_data, or :egress

  • (defaults to: nil)

    Description of what caused this flag to be enabled

Returns:

  • self for chaining

Raises:

  • if enabling would create lethal trifecta



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
# File 'lib/aidp/security/trifecta_state.rb', line 64

def enable(flag, source: nil)
  raise FrozenStateError, "Cannot modify frozen trifecta state" if @frozen

  if would_create_trifecta?(flag)
    raise PolicyViolation.new(
      flag: flag,
      source: source,
      current_state: to_h,
      message: build_violation_message(flag, source)
    )
  end

  case flag
  when :untrusted_input
    @untrusted_input = true
    @untrusted_input_source = source
  when :private_data
    @private_data = true
    @private_data_source = source
  when :egress
    @egress = true
    @egress_source = source
  else
    raise ArgumentError, "Unknown trifecta flag: #{flag}"
  end

  log_flag_enabled(flag, source)
  self
end

#enabled_countInteger

Count of currently enabled flags

Returns:

  • 0, 1, 2, or 3



55
56
57
# File 'lib/aidp/security/trifecta_state.rb', line 55

def enabled_count
  [untrusted_input, private_data, egress].count(true)
end

#freeze!Object

Freeze state - no further modifications allowed Used when passing state to execution context



120
121
122
123
# File 'lib/aidp/security/trifecta_state.rb', line 120

def freeze!
  @frozen = true
  self
end

#frozen?Boolean

Check if state is frozen

Returns:



126
127
128
# File 'lib/aidp/security/trifecta_state.rb', line 126

def frozen?
  @frozen
end

#lethal_trifecta?Boolean

Check if the lethal trifecta is currently active

Returns:

  • true if all three flags are enabled



49
50
51
# File 'lib/aidp/security/trifecta_state.rb', line 49

def lethal_trifecta?
  untrusted_input && private_data && egress
end

#status_stringObject

Human-readable status string



159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/aidp/security/trifecta_state.rb', line 159

def status_string
  flags = []
  flags << "untrusted_input" if untrusted_input
  flags << "private_data" if private_data
  flags << "egress" if egress

  if flags.empty?
    "No flags enabled (safe)"
  elsif lethal_trifecta?
    "LETHAL TRIFECTA: #{flags.join(", ")}"
  else
    "Enabled: #{flags.join(", ")} (#{enabled_count}/3 - safe)"
  end
end

#to_hObject

Export state as hash for logging/serialization



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/aidp/security/trifecta_state.rb', line 143

def to_h
  {
    work_unit_id: @work_unit_id,
    untrusted_input: @untrusted_input,
    untrusted_input_source: @untrusted_input_source,
    private_data: @private_data,
    private_data_source: @private_data_source,
    egress: @egress,
    egress_source: @egress_source,
    enabled_count: enabled_count,
    lethal_trifecta: lethal_trifecta?,
    frozen: @frozen
  }
end

#would_create_trifecta?(flag) ⇒ Boolean

Check if enabling a flag would create the lethal trifecta

Parameters:

  • :untrusted_input, :private_data, or :egress

Returns:

  • true if enabling would create lethal trifecta



34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/aidp/security/trifecta_state.rb', line 34

def would_create_trifecta?(flag)
  case flag
  when :untrusted_input
    private_data && egress
  when :private_data
    untrusted_input && egress
  when :egress
    untrusted_input && private_data
  else
    raise ArgumentError, "Unknown trifecta flag: #{flag}"
  end
end