Skip to content

Show scanner metadata in corgea inspect --issue - #183

Merged
Ibrahimrahhal merged 2 commits into
mainfrom
cursor/inspect-issue-scanner-metadata-6c1d
Oct 5, 2026
Merged

Ibrahimrahhal merged 2 commits into
mainfrom
cursor/inspect-issue-scanner-metadata-6c1d

Conversation

@Ibrahimrahhal

Copy link
Copy Markdown
Member

Summary

The issue API (GET /api/v1/issue/<id>) returns scanner identifiers such as Fortify's instance_id, class_id, kingdom, category, analyzer and build_id, but the CLI's Issue model didn't declare those fields. Serde dropped them, so they were missing even from --json output.

Changes

  • get_issue now sends include_metadata=true, so doghouse also returns issue.metadata, the identifiers stored on the issue when it was ingested. scanner_metadata, the fix-analysis copy, is always returned. Backends that don't recognize the parameter ignore it.
  • Issue gains two optional fields, scanner_metadata and metadata, so corgea inspect --issue --json passes both through. Both use skip_serializing_if, so corgea list --json output doesn't change, because the list endpoints don't send them.
  • The inspect --issue summary (both the default view and --summary) prints a "Scanner Metadata" section that merges the two sources. When a key appears in both, the issue's own value wins. Null and empty values are skipped.
  • skills/corgea/SKILL.md documents the new output.

Example:

Scanner Metadata:
  analyzer     : content
  build_id     : TODS
  category     : Cross-Site Request Forgery
  class_id     : 78E0700E-56FE-45A2-A11B-6A560F730576
  instance_id  : 6B481068C7D139B941ADF736E39E0676
  kingdom      : Encapsulation
  scanner      : fortify
-------------------------

Testing

  • ./harness check passes: strict clippy, format and all 944 tests.
  • New end-to-end tests in tests/cloud_commands_e2e/inspect.rs check that the request sends include_metadata=true, that --json includes both metadata objects, and that --summary prints the merged section with the expected precedence and skips nulls.
Open in Web Open in Cursor 

Request include_metadata=true from the issue API and keep both the
issue's scanner identifiers (metadata) and the fix analysis copy
(scanner_metadata) on the Issue model, so --json passes them through and
the summary prints a Scanner Metadata section.

Co-authored-by: ibrahim <ibrahim@corgea.com>
@Ibrahimrahhal
Ibrahimrahhal marked this pull request as ready for review October 4, 2026 11:47
@corgea-security corgea-security added the dennis-reviewed Dennis completed an automated review label Oct 4, 2026

@corgea-security corgea-security left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review risk: 1/5.

The change consistently requests, deserializes, merges, displays, and tests scanner metadata. No actionable defects are evident in the supplied diff.

No critical or high-priority changes were found.

@corgea-security corgea-security left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved by Dennis: high policy risk and automated risk 1/5.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scanner_metadata is deserialized onto the shared Issue struct and then serialized again by corgea list --json and corgea scan --out-format json. That changes those outputs for every issue the API attaches scanner metadata to, and the blocking-rules list path drops the field. inspect --issue itself is fine.

Open in Web View Automation 

Sent by Cursor Automation: pr-flow

Comment thread src/utils/api.rs Outdated
Comment on lines +2115 to +2121
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scanner_metadata: Option<serde_json::Map<String, Value>>,
/// Identifiers the originating scanner reported for the finding (e.g.
/// Fortify `instance_id`, `class_id`, `kingdom`). Sent only when the issue
/// is fetched with `include_metadata=true`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Map<String, Value>>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip_serializing_if = "Option::is_none" only drops these maps when they are missing. The list schema (Issue, used by GET /issues, GET /scan/{id}/issues, and GET /issues/code-quality) includes scanner_metadata. corgea list --json then re-serializes that struct:

let mut json = serde_json::json!({
    "page": issues_response.page,
    "total_pages": issues_response.total_pages,
    "results": &issues_response.issues
});

(src/list.rs around line 228). corgea scan --out-format json does the same with serde_json::to_string_pretty(&issues) in src/scanners/blast.rs around line 773. A Fortify or Checkmarx issue that has scanner metadata therefore grows a scanner_metadata object in both outputs. The new tests stub GET /issue/{id} only, so they stay green.

When the scan has blocking rules, list rebuilds each row as IssueWithBlockingRules (src/list.rs around line 239), which has no such field. The same issue then includes scanner_metadata on a non-blocking list and loses it on a blocking one.

Keep the maps for inspect display, but do not let Issue's serialize impl emit them. Apply this together with the inspect --json change below; the attribute change alone fails inspect_issue_json_includes_scanner_metadata.

Suggested change
#[serde(default, skip_serializing_if = "Option::is_none")]
pub scanner_metadata: Option<serde_json::Map<String, Value>>,
/// Identifiers the originating scanner reported for the finding (e.g.
/// Fortify `instance_id`, `class_id`, `kingdom`). Sent only when the issue
/// is fetched with `include_metadata=true`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<serde_json::Map<String, Value>>,
/// Scanner identifiers recorded by the fix analysis. Only the single-issue
/// endpoint sends it.
#[serde(default, skip_serializing)]
pub scanner_metadata: Option<serde_json::Map<String, Value>>,
/// Identifiers the originating scanner reported for the finding (e.g.
/// Fortify `instance_id`, `class_id`, `kingdom`). Sent only when the issue
/// is fetched with `include_metadata=true`.
#[serde(default, skip_serializing)]
pub metadata: Option<serde_json::Map<String, Value>>,

In src/inspect.rs, copy the maps back onto the printed object (this is the only user-facing JSON that should contain them):

if *json {
    let mut body = serde_json::to_value(&issue_details).unwrap();
    if let Some(issue) = body
        .get_mut("issue")
        .and_then(|value| value.as_object_mut())
    {
        if let Some(scanner_metadata) = &issue_details.issue.scanner_metadata {
            issue.insert(
                "scanner_metadata".to_string(),
                serde_json::Value::Object(scanner_metadata.clone()),
            );
        }
        if let Some(metadata) = &issue_details.issue.metadata {
            issue.insert(
                "metadata".to_string(),
                serde_json::Value::Object(metadata.clone()),
            );
        }
    }
    println!("{}", serde_json::to_string_pretty(&body).unwrap());
    return;
}

Issue is re-serialized by list --json and scan --out-format json.
skip_serializing_if still wrote scanner_metadata when the list API sent
it, and the blocking-rules path dropped the field again. inspect --json
copies the maps back onto its own payload.

Co-authored-by: Juan Gaitán <juan@corgea.com>
@Ibrahimrahhal
Ibrahimrahhal merged commit 1d7885d into main Oct 5, 2026
25 checks passed
@Ibrahimrahhal
Ibrahimrahhal deleted the cursor/inspect-issue-scanner-metadata-6c1d branch October 5, 2026 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dennis-reviewed Dennis completed an automated review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants