security: hard-disable all KIS trading paths (AEG-X-016)

Blocks submit, status, cancel, and settlement before HTTP or database writes and removes the KIS polling recurring job. Evidence: concrete adapter test 1/1 passed with zero HTTP calls. WBS remains IN_PROGRESS pending endpoint/startup override evidence.
This commit is contained in:
2026-08-09 02:30:39 +09:00
parent ec80337389
commit eb59cae8e3
8 changed files with 113 additions and 3 deletions
@@ -25,6 +25,17 @@ public class KisTradeExecutionException : Exception
}
}
public sealed class KisTradingDisabledException : InvalidOperationException
{
public KisTradingDisabledException()
: base("KIS trading is hard-disabled. No buy, sell, cancel, status, or settlement call is permitted.") { }
}
public static class KisTradingHardOff
{
public static void EnsureDisabled() => throw new KisTradingDisabledException();
}
public interface IKisTradeExecutionService
{
Task<(string OrderId, JsonElement Response)> ExecuteTradeAsync(Guid tradeId, int quantity, decimal limitPrice, Guid correlationId, CancellationToken ct = default);
@@ -56,6 +67,7 @@ public class KisTradeExecutionService : IKisTradeExecutionService
Guid correlationId,
CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var requestBody = new
{
symbol = "US0100",
@@ -120,6 +132,7 @@ public class KisTradeExecutionService : IKisTradeExecutionService
Guid correlationId,
CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var request = new HttpRequestMessage(HttpMethod.Get, $"{KisApiBase}/orders/{kisOrderId}");
request.Headers.Add("X-Correlation-ID", correlationId.ToString());
@@ -159,6 +172,7 @@ public class KisTradeExecutionService : IKisTradeExecutionService
Guid correlationId,
CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var requestBody = new { reason = reason };
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
@@ -195,6 +209,7 @@ public class KisTradeExecutionService : IKisTradeExecutionService
Guid correlationId,
CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var requestBody = new { confirm = true };
var content = new StringContent(
JsonSerializer.Serialize(requestBody),
@@ -42,6 +42,7 @@ public class SubmitTradeHandler
public async Task<Guid> HandleAsync(SubmitTradeCommand command, CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var trade = Trade.Create(command.SellDecisionId, command.Quantity, command.CorrelationId, _clock.UtcNow.UtcDateTime);
await _sql.InsertTradeAsync(trade, ct);
_logger.LogInformation("Created trade: {TradeId}", trade.Id);
@@ -127,6 +128,7 @@ public class PollTradeStatusHandler
public async Task HandleAsync(PollTradeStatusCommand command, CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var trade = await _sql.GetTradeByIdAsync(command.TradeId, command.CorrelationId, ct);
if (trade == null)
{
@@ -222,6 +224,7 @@ public class ConfirmSettlementHandler
public async Task HandleAsync(ConfirmSettlementCommand command, CancellationToken ct = default)
{
KisTradingHardOff.EnsureDisabled();
var trade = await _sql.GetTradeByIdAsync(command.TradeId, command.CorrelationId, ct);
if (trade == null)
{