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:

  • flag (Symbol)

    :untrusted_input, :private_data, or :egress

Returns:

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:

  • flag (Symbol)

    :untrusted_input, :private_data, or :egress

  • source (String) (defaults to: nil)

    Description of what caused this flag to be enabled

Returns:

Raises:



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:

  • (Integer)

    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:

  • (Boolean)


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:

  • (Boolean)

    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:

  • flag (Symbol)

    :untrusted_input, :private_data, or :egress

Returns:

  • (Boolean)

    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