This page captures the 20 business rules enforced by SAIF.Web.API, covering response normalization, correlation and observability, HTTP-status mapping, transport security, and delegated role-based authorization.
BR-WEBAPI-001 Server-Name header on every response¶
Field
Value
Statement
Every response emitted through the handler must carry a Server-Name header identifying the serving machine.
Trigger
Any response reaching the end of BuildApiResponse.
BR-WEBAPI-002 Normalize non-success responses into ErrorResponse¶
Field
Value
Statement
An unsuccessful response whose content is an HttpError must be replaced by a normalized ErrorResponse body (ErrorCode, ErrorMessage, ErrorId, ErrorUrl) with the original response headers preserved.
BR-WEBAPI-003 Client-fault exception types map to 400 Bad Request¶
Field
Value
Statement
An error whose ExceptionType name ends with InvalidDataException, ODataException, or ArgumentException is a client fault and must be reported as 400 Bad Request.
BR-WEBAPI-005 Unique correlation identifier per response¶
Field
Value
Statement
Each processed response must receive a unique, time-ordered correlation identifier composed of a local timestamp (yyyyMMddHHmmssffffzzz) and a GUID, with : removed.
Trigger
Every BuildApiResponse invocation.
Enforcement
ApiResponseHandler.GenerateId (protected virtual)
Outcome
MessageID set; surfaced to the client as ErrorResponse.ErrorId.
A Splunk lookup URL for the correlation ID must always be produced; when the environment service is unavailable or the splunksearch setting is blank, hard-coded defaults are used.
Trigger
Every BuildApiResponse invocation.
Enforcement
ApiResponseHandler.BuildMessageURL
Outcome
MessageIDUrl set; surfaced as ErrorResponse.ErrorUrl.
BR-WEBAPI-007 Redact password material from debug traces¶
Field
Value
Statement
Password material in the logged request input stream must be redacted before the trace message is emitted.
Trigger
BuildTraceMessage with debugMode = true.
Enforcement
ApiResponseHandler.BuildTraceMessage
Outcome
Text matching password[^,\n]* (case-insensitive) is replaced with "Password": "********". Scope limit: applied only to the input-stream copy, not to Request Content / Response Content.
Every normalized error must be logged through the available log service; when the service also implements ILogServiceForApi, the correlation ID is passed so the log entry can be joined to the client's ErrorId.
Trigger
Error branch of BuildApiResponse, logService != null.
Enforcement
ApiResponseHandler.BuildApiResponse
Outcome
Exception logged with full debug trace; logged twice when the service implements both contracts.
BR-WEBAPI-009 Trace every request with debug-gated verbosity¶
Field
Value
Statement
Every request (successful or not) must produce a trace entry when ILogService is resolved (no trace is emitted otherwise); the verbose payload (bodies, headers, input stream) is included only when the log service threshold is at debug.
BR-WEBAPI-010 Developer guidance text when stack trace is unavailable¶
Field
Value
Statement
When an error carries no stack trace and IncludeErrorDetailPolicy is not Always, the logged exception text must be replaced with a fixed developer-guidance message explaining that customErrors is suppressing detail.
Trigger
GetExceptionMessage evaluation.
Enforcement
ApiResponseHandler.GetExceptionMessage
Outcome
Guidance text instructing the developer to set config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;.
BR-WEBAPI-011 SAIF exception types map to fixed HTTP status codes¶
Field
Value
Statement
SAIF API exception types map to fixed HTTP status codes: InternalServerError→500, NoMatchingDataException→404, UnauthorizedAccessException→403, InvalidDataException→400; anything else→500.
Trigger
ExceptionHandler.ToHttpError called by a consumer.
src/SAIF.Web.API/ExceptionHandler.cs (types from SAIF.API.Exceptions)
BR-WEBAPI-012 HTTPS required unless local exemption applies¶
Field
Value
Statement
HTTPS is required for every decorated action unless the request is local and either no RequireSSLForLocal app-setting exists or its value parses to false.
BR-WEBAPI-014 Authorization fails closed on missing dependencies¶
Field
Value
Statement
Authorization must fail closed: a missing ISecurityApiClient or IEnvironmentService in the request scope is an error, not an implicit allow; actionContext and (in the unauthorized path) ILogService must be non-null.
BR-WEBAPI-020 Role names are trimmed and normalized¶
Field
Value
Statement
Role names supplied to [SaifAuthorize] are whitespace-trimmed at construction and again space-stripped at evaluation, so callers may pass padded role strings.
Trigger
Attribute construction / evaluation.
Enforcement
SaifAuthorizeAttribute constructor and IsAuthorized
The forceProductionRole constructor parameter on SaifAuthorizeAttribute implies an intended rule ("evaluate against production role names even in non-production"), but the stored _ForceProductionRole field is never read anywhere in the repository. This is recorded as an open question rather than a rule, per the analysis's convention that unreached behavior is not a rule.
ExceptionHandler.ToHttpError maps SAIF.API.Exceptions.UnauthorizedAccessException → 403 Forbidden (BR-WEBAPI-011), while SaifAuthorizeAttribute.HandleUnauthorizedRequest returns 401 Unauthorized (BR-WEBAPI-018) — the two authorization-denial paths disagree on status code. This is an observed inconsistency, not a defect claim.*
Note: Statements marked with * were inferred from code structure, naming conventions, or
contextual evidence where direct confirmation was not available in source artifacts.