Skip to content

Comments

[backend] feat(sc): post-creation redirection and CTAs for security coverage (#4676)#4980

Open
GaetanSantucci wants to merge 2 commits intorelease/currentfrom
issue/4676
Open

[backend] feat(sc): post-creation redirection and CTAs for security coverage (#4676)#4980
GaetanSantucci wants to merge 2 commits intorelease/currentfrom
issue/4676

Conversation

@GaetanSantucci
Copy link
Member

As a SOC Analyst
I want 2 clear CTA after creating a Security Coverage, one displayed in the creation report and one available in the Security Coverage Overview, when I am redirected there
So that I can immediately review my generated security coverage

Proposed changes

Added a method returning a Stix bundle with the external URL leading to the scenario created from the security coverage, necessary for redirection to OpenAEV from OpenCTI.

Testing Instructions

  1. OpenCTI environment needed
  2. Create a security coverage
  3. A cta Go to openaev with scenario url appears
  4. Open and navigate to new tab

Related issues

@GaetanSantucci GaetanSantucci self-assigned this Feb 19, 2026
@GaetanSantucci GaetanSantucci changed the title [backend] feat(SC): post-creation redirection and CTAs for security coverage (#4676) [backend] feat(sc): post-creation redirection and CTAs for security coverage (#4676) Feb 19, 2026
@codecov
Copy link

codecov bot commented Feb 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.53%. Comparing base (f4f6ac1) to head (6a598ff).

Additional details and impacted files
@@                  Coverage Diff                  @@
##             release/current    #4980      +/-   ##
=====================================================
+ Coverage              56.38%   56.53%   +0.15%     
- Complexity              4505     4514       +9     
=====================================================
  Files                    999      999              
  Lines                  29847    29856       +9     
  Branches                2177     2177              
=====================================================
+ Hits                   16828    16878      +50     
+ Misses                 12058    12013      -45     
- Partials                 961      965       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the STIX security coverage ingestion flow to push an updated STIX object back to OpenCTI containing an external URL to the generated OpenAEV scenario, enabling post-creation CTAs/redirection from OpenCTI.

Changes:

  • Add SecurityCoverageService#pushCoverageToOpenCTI(...) to inject external_uri into the Security Coverage SDO and push a STIX bundle to OpenCTI.
  • Update StixService#processBundle(...) to call the OpenCTI push after building the scenario.
  • Adjust integration test fixtures and STIX API integration tests to enable OpenCTI mode and prepare a “manual” injector contract.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
openaev-api/src/test/java/io/openaev/utils/fixtures/opencti/TestBeanConnector.java Makes the test connector URL configurable via Spring property injection.
openaev-api/src/test/java/io/openaev/utils/fixtures/InjectorContractFixture.java Adds a helper to ensure a well-known manual injector contract exists for tests.
openaev-api/src/test/java/io/openaev/api/stix_process/StixApiTest.java Enables OpenCTI mode for integration tests and adds a MockServer to emulate OpenCTI endpoints.
openaev-api/src/main/java/io/openaev/service/stix/StixService.java Pushes coverage back to OpenCTI as part of STIX bundle processing.
openaev-api/src/main/java/io/openaev/service/stix/SecurityCoverageService.java Implements the OpenCTI push: parse coverage SDO, set external_uri, wrap into bundle, send via OpenCTIConnectorService.
Comments suppressed due to low confidence (3)

openaev-api/src/main/java/io/openaev/service/stix/SecurityCoverageService.java:325

  • The Javadoc for pushCoverageToOpenCTI lists ParsingException and ConnectorError, but the method signature also throws IOException (from the underlying OpenCTI client). Update the Javadoc to document IOException as well so callers understand the full error contract.
  /**
   * Pushes the security coverage to OpenCTI. This injects the OpenAEV scenario external URL into
   * the STIX object.
   *
   * @param scenario The scenario containing the security coverage.
   * @throws ParsingException If STIX parsing fails.
   * @throws ConnectorError If the OpenCTI push fails.
   */
  public void pushCoverageToOpenCTI(Scenario scenario)
      throws ParsingException, ConnectorError, IOException {
    SecurityCoverage coverage = scenario.getSecurityCoverage();

openaev-api/src/test/java/io/openaev/api/stix_process/StixApiTest.java:136

  • Because setUp() runs before every test, the two mockServer.when(...) expectations are appended repeatedly. In this file (which contains many tests), that can grow the expectation list and slow down or destabilize the suite. Consider calling mockServer.reset() (or mockServer.resetExpectations()) at the start of setUp() before adding new expectations.
  @BeforeEach
  void setUp() throws Exception {

    attackPatternComposer.reset();

openaev-api/src/main/java/io/openaev/service/stix/StixService.java:40

  • processBundle now propagates ConnectorError, but the current STIX controller (StixApi#processBundle) only treats BadRequestException | ParsingException | IOException as a 400 and will handle ConnectorError via the generic Exception branch (500). If connector errors should be returned/acknowledged differently (or not fail the whole operation), handle ConnectorError explicitly (either here or in the controller) and decide whether scenario creation should roll back on OpenCTI push failures.
  public Scenario processBundle(String stixJson)
      throws IOException, ParsingException, ConnectorError {

    try {
      // Update securityCoverage with the last bundle
      SecurityCoverage securityCoverage =
          securityCoverageService.processAndBuildStixToSecurityCoverage(stixJson);

      // Update Scenario using the last SecurityCoverage
      Scenario scenario =
          securityCoverageService.buildScenarioFromSecurityCoverage(securityCoverage);
      securityCoverageService.pushCoverageToOpenCTI(scenario);
      return scenario;

Comment on lines +228 to +241
// need to mock unregistered connector to be use in process
mockServer
.when(request().withMethod("POST").withPath(""))
.respond(
response()
.withStatusCode(200)
.withHeader("Content-Type", "application/json")
.withBody(
"""
{
"data": {}
}
"""));
openCTIConnectorService.registerOrPingAllConnectors();
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

MockServer expectations are set up with withPath(""), but the OpenCTI GraphQL client posts to a URL ending with /graphql (see OpenCTIConfig#getApiUrl) and will therefore request path /graphql or /. As written, this expectation likely won’t match the real requests, causing connector registration/push to fail and the test to be flaky/failing. Consider matching the actual paths (e.g. / and /graphql) or using a broader matcher, and ensure the matching expectation is configured for the request you intend to intercept.

Copilot uses AI. Check for mistakes.
Comment on lines +241 to +245
openCTIConnectorService.registerOrPingAllConnectors();

mockServer
.when(request().withMethod("POST").withPath("graphql"))
.respond(response().withStatusCode(200));
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

registerOrPingAllConnectors() is invoked before the /graphql expectation is configured. Since connector registration calls the GraphQL endpoint, the registration HTTP call won’t be stubbed here and may fail, leaving the connector unregistered and breaking later pushes. Configure the GraphQL expectation before calling registerOrPingAllConnectors() (or move the registration call after all relevant expectations are set).

Suggested change
openCTIConnectorService.registerOrPingAllConnectors();
mockServer
.when(request().withMethod("POST").withPath("graphql"))
.respond(response().withStatusCode(200));
mockServer
.when(request().withMethod("POST").withPath("graphql"))
.respond(response().withStatusCode(200));
openCTIConnectorService.registerOrPingAllConnectors();

Copilot uses AI. Check for mistakes.

mockServer
.when(request().withMethod("POST").withPath("graphql"))
.respond(response().withStatusCode(200));
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

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

The MockServer response for the GraphQL endpoint is 200 with an empty body. OpenCTIClient expects a GraphQL-shaped JSON response containing at least a data or errors field; an empty body will be treated as an error response, causing registerConnector/pushStixBundle to throw ConnectorError. Return a minimal valid GraphQL JSON payload (e.g., { "data": {} }) for the stubbed /graphql requests.

Suggested change
.respond(response().withStatusCode(200));
.respond(
response()
.withStatusCode(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"data\":{}}"));

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant