CWE-223: Omission of Security-relevant Information
Overview
Omission of security-relevant information occurs when a product fails to record or display details that would be needed to identify the source or nature of an attack, or to let someone judge whether an action was safe. The most common instance of this weakness is missing security event logging - see CWE-778 for that specific case. This page covers the broader remainder: security-relevant detail dropped from something a human or downstream system consults at the point of a decision - a security warning, a consent prompt, a scanner report, an audit export - rather than from an event log.
Relationship to Other CWEs
CWE-223 is a child of CWE-221 (Information Loss or Omission), which has no page here, and the parent of CWE-778 (Insufficient Logging), the specific case where the omitted information is a security event log entry. Most findings reported as CWE-223 are really CWE-778's narrower case: if the finding is about a missing or incomplete log entry, use CWE-778 rather than this page. MITRE also lists CWE-1429 (Missing Security-Relevant Feedback for Unexecuted Operations in Hardware Interface) as a further child with no page here yet.
OWASP Classification
A09:2025 - Security Logging & Alerting Failures
Risk
Medium: When a security warning, prompt, or report omits the specific detail behind it, the person or system relying on that output cannot judge whether the situation is safe - they either proceed blind or over-trust a generic-looking indicator. This delays incident response, causes users to click through warnings they can't evaluate, and leaves auditors and downstream tooling working from an incomplete picture even when something was recorded or shown.
Remediation Steps
Core Principle: Any output whose purpose is to help someone judge safety or attribute an action - a warning, a prompt, a report, an indicator - must carry the specific reason behind it, not just a generic "this may be unsafe" signal.
Identify Where Detail Is Being Dropped
- Security warnings and prompts: a TLS/certificate warning, a file-download warning, a permission-consent dialog that shows a generic message without the specific condition that triggered it
- Scanner and audit output: a security tool, linter, or compliance report that surfaces a pass/fail or severity level but drops the specific rule, file, or field that caused the finding
- Downstream/API consumers: an internal API or webhook that summarizes a security decision (e.g., "blocked") for another system without including enough detail for that system, or a human reviewing its output, to act on it
- If the missing detail is absent from an event log used for later investigation, that is CWE-778, not this page
Include the Specific Reason, Not Just a Generic Signal (Primary Defense)
- Certificate/TLS warnings should state which check failed - expired, wrong hostname, untrusted CA, revoked - not just "connection not secure"
- Permission and consent prompts should name the specific capability being granted (e.g., "read your contacts," not "grant permission")
- Security scanner and compliance report output should include the specific rule, file/line, and severity for every finding it surfaces, not an aggregate score alone
- File-type, macro, and script warnings should name the specific indicator that triggered them (extension mismatch, signature missing, macro present) rather than a blanket warning
- Where the recipient may be the attacker, the detail belongs somewhere else, not nowhere: a login, password-reset, or account-lookup response has to stay identical across causes, because naming the cause enumerates accounts - see CWE-209. Collapse the message returned to the untrusted party and keep the specific reason where the reader is entitled to it: the server-side log, an admin view, or the authenticated user's own security page
Preserve Detail Through the Full Output Path
- Confirm that detail captured at the point of detection survives serialization, truncation, and any UI/report template rendering step before it reaches the person or system consuming it
- Do not summarize a finding down to a boolean or a single severity level before it is displayed or exported - preserve the underlying detail alongside the summary
- Where several conditions produce the same generic warning, check that each one still carries its own distinguishing detail through to the output
Test the Fix
- Trigger each warning/prompt/report path under its specific failure condition and confirm the output names that condition, not a generic message shared by different causes
- Confirm a downstream consumer (log aggregator, SIEM, reviewing engineer) can distinguish two underlying causes that previously produced the same generic output
- Re-scan or re-audit to confirm the finding is resolved
Common Vulnerable Patterns
// VULNERABLE - pseudo-code
function check_certificate(cert):
if not certificate_valid(cert):
return warning("Connection is not secure") // same message whether expired, wrong host, or untrusted CA
function scan_result_summary(findings):
return { status: any(f.severity == "high" for f in findings) ? "fail" : "pass" }
// the specific findings - which rule, which file - never reach the report
Why this is vulnerable: Both outputs tell the reader that something is wrong without telling them what. A user seeing the generic certificate warning cannot tell an internal self-signed cert they recognize from an active MITM attack; the information that would let them tell those apart was available at detection time and discarded before display. The scanner summary is equally unusable to a reviewer: "fail" gives no starting point for remediation.
Secure Patterns
// SECURE - pseudo-code
function check_certificate(cert):
failure_reason = validate_certificate(cert) // returns the specific check that failed, or null
if failure_reason:
return warning("Connection is not secure: " + failure_reason.description, code=failure_reason.code)
// e.g. "certificate expired 2026-01-15" or "hostname mismatch: expected api.example.com, got other.example.com"
function scan_result_summary(findings):
return {
status: any(f.severity == "high" for f in findings) ? "fail" : "pass",
findings: [{rule: f.rule_id, file: f.file, line: f.line, severity: f.severity} for f in findings]
}
Why this works: The certificate warning now carries the specific check that failed, giving the reader enough information to judge the actual risk rather than reacting to an undifferentiated alarm. The scan summary preserves every underlying finding alongside the pass/fail rollup, so a reviewer or downstream tool can act on the report instead of just being told something failed somewhere.
Common Pitfalls
- Collapsing every failure reason into one generic message "for simplicity": a single "access denied" or "invalid" message that covers several genuinely different underlying conditions saves a little UI text at the cost of making every one of those conditions equally hard to diagnose. The exception is where the recipient may be the attacker: identical authentication and account-lookup messages are required there, not a shortcut (CWE-209), and the fix is to route the detail to the log or an authorized view, not to discard it.
- Logging the detail but not surfacing it anywhere a human reviews: capturing the specific reason in a debug-level log line that nobody reads, while the security-relevant prompt or report the person actually sees stays generic, doesn't solve the omission - it just relocates the missing information one layer down.
- Truncating detail at the display layer after preserving it upstream: a report generator or UI template that only renders the top-level status field, silently dropping a
detailsorfindingsarray the backend already populated correctly. - Treating "we show a warning" as equivalent to "we show enough to act on": confirming a warning fires under the vulnerable condition is not the same as confirming the warning tells the reader which condition fired - test for the second, not just the first.