check method

  1. @override
Future<SecurityResult> check(
  1. RequestContext context
)
override

Inspects context and returns a SecurityResult.

Implementations should be side-effect-light with respect to context.body (avoid destructive mutation) but may freely read/write context.metadata to communicate with later layers.

Implementation

@override
Future<SecurityResult> check(RequestContext context) async {
  final body = context.getMeta<dynamic>('sanitizedBody') ?? context.body;
  final amount = Validators.extractAmount(body) ?? 0.0;
  final requestFrequency = context.getMeta<int>('requestFrequency') ?? 1;

  final isNewDevice = await _isNewDevice(context);
  final isIpAnomaly = await _isIpAnomaly(context);

  final features = RiskFeatures(
    amount: amount,
    requestFrequency: requestFrequency,
    isNewDevice: isNewDevice,
    isIpAnomaly: isIpAnomaly,
  );
  context.setMeta('riskFeatures', features.toJson());

  final score = _computeRiskScore(features);
  context.setMeta('riskScore', score);

  // Record this device/IP as known for the user going forward, so
  // repeat requests from the same device/IP are no longer "new".
  await _rememberDeviceAndIp(context);

  final flags = <String>[];
  if (features.amount > highAmountThreshold) flags.add('high_amount');
  if (features.isNewDevice) flags.add('new_device');
  if (features.isIpAnomaly) flags.add('ip_anomaly');
  if (features.requestFrequency > highFrequencyThreshold) {
    flags.add('high_frequency');
  }

  if (score > blockThreshold) {
    return SecurityResult.block(
      message: 'Anomaly score too high (${score.toStringAsFixed(2)})',
      flags: flags,
      riskScore: score,
    );
  }

  if (score > flagThreshold) {
    return SecurityResult.flag(
      message:
          'Request flagged for review (score ${score.toStringAsFixed(2)})',
      flags: flags,
      riskScore: score,
    );
  }

  return SecurityResult.allow(
    message: 'No significant anomaly detected',
    riskScore: score,
    flags: flags,
  );
}