Module: PgSqlTriggers::SQL::KillSwitch

Defined in:
lib/pg_sql_triggers/sql/kill_switch.rb

Overview

KillSwitch: three-layer safety gate for dangerous operations.

Layer 1 – config: PgSqlTriggers.kill_switch_enabled / kill_switch_environments Layer 2 – ENV: KILL_SWITCH_OVERRIDE=true (+ optional confirmation) Layer 3 – explicit: confirmation text passed directly to check!

Examples:

KillSwitch.check!(operation: :migrate_up, environment: Rails.env,
                  confirmation: params[:confirmation_text],
                  actor: { type: "UI", id: current_user.email })

Constant Summary collapse

OVERRIDE_KEY =
:pg_sql_triggers_kill_switch_override

Class Method Summary collapse

Class Method Details

.active?(environment: nil, operation: nil) ⇒ Boolean

Returns:

  • (Boolean)


121
122
123
124
125
126
127
128
# File 'lib/pg_sql_triggers/sql/kill_switch.rb', line 121

def active?(environment: nil, operation: nil)
  return false unless kill_switch_enabled?

  env    = resolve_environment(environment)
  active = protected_environment?(env)
  logger&.debug "[KILL_SWITCH] Check: operation=#{operation} environment=#{env} active=#{active}" if operation
  active
end

.check!(operation:, environment: nil, confirmation: nil, actor: nil) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/pg_sql_triggers/sql/kill_switch.rb', line 130

def check!(operation:, environment: nil, confirmation: nil, actor: nil)
  env = resolve_environment(environment)

  unless active?(environment: env, operation: operation)
    log(:info, "ALLOWED",
        operation: operation,
        environment: env,
        actor: actor,
        extra: "reason=not_protected_environment")
    return
  end

  if Thread.current[OVERRIDE_KEY]
    log(:warn, "OVERRIDDEN",
        operation: operation,
        environment: env,
        actor: actor,
        extra: "source=thread_local")
    return
  end

  if ENV["KILL_SWITCH_OVERRIDE"]&.downcase == "true"
    if confirmation_required?
      validate_confirmation!(confirmation, operation)
      log(:warn, "OVERRIDDEN",
          operation: operation,
          environment: env,
          actor: actor,
          extra: "source=env_with_confirmation confirmation=#{confirmation}")
    else
      log(:warn, "OVERRIDDEN",
          operation: operation,
          environment: env,
          actor: actor,
          extra: "source=env_without_confirmation")
    end
    return
  end

  unless confirmation.nil?
    validate_confirmation!(confirmation, operation)
    log(:warn, "OVERRIDDEN",
        operation: operation,
        environment: env,
        actor: actor,
        extra: "source=explicit_confirmation confirmation=#{confirmation}")
    return
  end

  log(:error, "BLOCKED", operation: operation, environment: env, actor: actor)
  expected = expected_confirmation(operation)
  raise PgSqlTriggers::KillSwitchError.new(
    "Kill switch is active for #{env} environment. Operation '#{operation}' has been blocked.\n\n" \
    "To override: KILL_SWITCH_OVERRIDE=true or provide confirmation text: #{expected}",
    error_code: "KILL_SWITCH_ACTIVE",
    recovery_suggestion: "Provide the confirmation text: #{expected}",
    context: { operation: operation, environment: env, expected_confirmation: expected }
  )
end

.override(confirmation: nil) ⇒ Object

Raises:

  • (ArgumentError)


190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/pg_sql_triggers/sql/kill_switch.rb', line 190

def override(confirmation: nil)
  raise ArgumentError, "Block required for kill switch override" unless block_given?

  if confirmation.present?
    logger&.info "[KILL_SWITCH] Override block initiated with confirmation: #{confirmation}"
  end
  previous = Thread.current[OVERRIDE_KEY]
  Thread.current[OVERRIDE_KEY] = true
  begin
    yield
  ensure
    Thread.current[OVERRIDE_KEY] = previous
  end
end