-
Notifications
You must be signed in to change notification settings - Fork 270
SchemaConform View Schema button #3583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
SchemaConform View Schema button #3583
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 AI Security analysis: "Automated security scan of the code changes found no issues. Continue with standard CI checks and monitor behavior after deployment."
| Risk Level | AI Score |
|---|---|
| 🟢 NO RISK | 5.0/100 |
Top 0 security issues / 0 total (Critical: 0, High: 0, Medium: 0, Low: 0)
| Title | Location | Recommendation |
|---|---|---|
| — | — | No issues to display |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 AI Security analysis: "Automated security scan of the code changes found no issues. Continue with standard CI checks and monitor behavior after deployment."
| Risk Level | AI Score |
|---|---|
| 🟢 NO RISK | 5.0/100 |
Top 0 security issues / 0 total (Critical: 0, High: 0, Medium: 0, Low: 0)
| Title | Location | Recommendation |
|---|---|---|
| — | — | No issues to display |
…hlight' into threat-activity-highlight
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤖 AI Security analysis: "Automated scanning reported no security findings for these changes. Residual risk remains because tools can miss logic errors, coverage gaps, or secrets; prioritize targeted manual review of security-critical areas and complementary dynamic and dependency scans in CI."
| Risk Level | AI Score |
|---|---|
| 🟢 NO RISK | 10.0/100 |
Top 0 security issues / 0 total (Critical: 0, High: 0, Medium: 0, Low: 0)
| Title | Location | Recommendation |
|---|---|---|
| — | — | No issues to display |
There was a problem hiding this 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 adds a "View Schema" button to the Schema Validation Errors banner that allows users to view the uploaded OpenAPI schema for an API collection. The feature enhances the threat detection page by enabling users to reference the schema when reviewing validation errors, and handles page refresh scenarios by fetching event metadata when context is unavailable.
Key Changes:
- Added a collapsible OpenAPI schema viewer within the Schema Validation Errors banner
- Implemented backend endpoint to fetch uploaded OpenAPI schemas from the database
- Enhanced ThreatDetectionPage to fetch event metadata on page refresh to ensure apiCollectionId is available
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
apps/dashboard/web/polaris_web/web/src/apps/dashboard/components/shared/SampleDataList.js |
Added View/Hide Schema button and collapsible schema viewer to SchemaValidationError component with async schema fetching |
apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/observe/api.js |
Added fetchOpenApiSchema API method to retrieve schema from backend |
apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/components/SampleDetails.jsx |
Passed apiCollectionId prop to SampleDataList component |
apps/dashboard/web/polaris_web/web/src/apps/dashboard/pages/threat_detection/ThreatDetectionPage.jsx |
Added event metadata fetching on page refresh to retrieve apiCollectionId when rowContext is unavailable |
apps/dashboard/src/main/java/com/akto/action/OpenApiAction.java |
Implemented fetchOpenApiSchema method to retrieve and decompress stored OpenAPI schema |
apps/dashboard/src/main/resources/struts.xml |
Added Struts action mapping for fetchOpenApiSchema endpoint with READ access control |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const response = await api.fetchOpenApiSchema(apiCollectionId); | ||
| setSchemaContent(response?.openApiSchema || "No schema available"); | ||
| } catch (error) { | ||
| setSchemaContent("Error loading schema: " + error.message); |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an error occurs, the error message is set as schemaContent and passed to the SampleData component with language="json". However, an error message string is not valid JSON. Consider either wrapping the error message in a proper format or conditionally rendering a different error UI:
} catch (error) {
setSchemaContent(JSON.stringify({ error: error.message }, null, 2));
}Or alternatively:
const [schemaError, setSchemaError] = useState(null);
// In catch block:
} catch (error) {
setSchemaError(error.message);
}
// In render:
{schemaError ? (
<Banner status="critical">{schemaError}</Banner>
) : (
<SampleData data={{ original: schemaContent }} ... />
)}| setSchemaContent("Error loading schema: " + error.message); | |
| setSchemaContent(JSON.stringify({ error: error.message }, null, 2)); |
| const eventListResponse = await api.fetchSuspectSampleData( | ||
| 0, // skip | ||
| [queryParams.actor], // ips filter | ||
| [], // apiCollectionIds | ||
| [], // urls | ||
| [], // types | ||
| {}, // sort | ||
| 0, // startTimestamp (use 0 to get all) | ||
| Math.floor(Date.now() / 1000), // endTimestamp (current time) | ||
| [queryParams.filterId], // latestAttack filter | ||
| 10, // limit | ||
| queryParams.status || 'ACTIVE', // statusFilter | ||
| undefined, // successfulExploit | ||
| LABELS.THREAT, // label | ||
| [], // hosts | ||
| undefined // latestApiOrigRegex | ||
| ); |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The event metadata fetch uses startTimestamp: 0 and endTimestamp: Date.now() to get all events, which could potentially retrieve a large dataset. Consider adding a more specific time range or using pagination parameters to limit the query scope. For example:
const eventListResponse = await api.fetchSuspectSampleData(
0, // skip
[queryParams.actor], // ips filter
[], // apiCollectionIds
[], // urls
[], // types
{}, // sort
Math.floor(Date.now() / 1000) - 86400, // startTimestamp (last 24 hours instead of all time)
Math.floor(Date.now() / 1000), // endTimestamp (current time)
[queryParams.filterId], // latestAttack filter
1, // limit - only need one matching event
queryParams.status || 'ACTIVE', // statusFilter
undefined, // successfulExploit
LABELS.THREAT, // label
[], // hosts
undefined // latestApiOrigRegex
);| const matchingEvent = eventListResponse?.maliciousEvents?.find(e => e.refId === queryParams.refId); | ||
| if (matchingEvent) { | ||
| eventMetadata = { | ||
| url: matchingEvent.url, | ||
| method: matchingEvent.method, | ||
| apiCollectionId: matchingEvent.apiCollectionId, | ||
| eventId: matchingEvent.id, | ||
| status: matchingEvent.status, | ||
| jiraTicketUrl: matchingEvent.jiraTicketUrl | ||
| }; | ||
| } |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the matchingEvent is not found in the response, eventMetadata will remain null, and the event will be displayed with empty metadata fields. Consider adding error handling to notify the user or retry with different parameters:
const matchingEvent = eventListResponse?.maliciousEvents?.find(e => e.refId === queryParams.refId);
if (matchingEvent) {
eventMetadata = {
url: matchingEvent.url,
method: matchingEvent.method,
apiCollectionId: matchingEvent.apiCollectionId,
eventId: matchingEvent.id,
status: matchingEvent.status,
jiraTicketUrl: matchingEvent.jiraTicketUrl
};
} else {
// Event not found - could be deleted or filtered out
throw new Error('Event not found');
}| <LegacyCard.Section> | ||
| <SampleData | ||
| data={{ original: schemaContent }} | ||
| language="json" |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SampleData component expects the editorLanguage prop, not language. Change language="json" to editorLanguage="json":
<SampleData
data={{ original: schemaContent }}
editorLanguage="json"
minHeight="30vh"
wordWrap={true}
/>Note: This same issue exists on line 135, but that's outside the scope of this PR.
| language="json" | |
| editorLanguage="json" |
| } | ||
| setLoadingSchema(false); |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a race condition in the handleViewSchema function. If the user clicks the button multiple times quickly before the API call completes, setLoadingSchema(false) on line 41 will be executed outside the try-catch block and could be called even if an error occurs. The state update should be in a finally block:
const handleViewSchema = async () => {
if (!schemaOpen && !schemaContent && apiCollectionId) {
setLoadingSchema(true);
try {
const response = await api.fetchOpenApiSchema(apiCollectionId);
setSchemaContent(response?.openApiSchema || "No schema available");
} catch (error) {
setSchemaContent("Error loading schema: " + error.message);
} finally {
setLoadingSchema(false);
}
}
setSchemaOpen(!schemaOpen);
};| } | |
| setLoadingSchema(false); | |
| } finally { | |
| setLoadingSchema(false); | |
| } |
| {apiCollectionId && ( | ||
| <Collapsible open={schemaOpen} id="schema-collapsible"> | ||
| <LegacyCard> | ||
| <LegacyCard.Section> | ||
| <SampleData | ||
| data={{ original: schemaContent }} | ||
| language="json" | ||
| minHeight="30vh" | ||
| wordWrap={true} | ||
| /> | ||
| </LegacyCard.Section> | ||
| </LegacyCard> | ||
| </Collapsible> | ||
| )} |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The SampleData component will receive null as data.original before the schema is fetched. Consider rendering the Collapsible only after the schema content is loaded to avoid displaying empty or loading content:
{apiCollectionId && schemaContent && (
<Collapsible open={schemaOpen} id="schema-collapsible">
<LegacyCard>
<LegacyCard.Section>
<SampleData
data={{ original: schemaContent }}
language="json"
minHeight="30vh"
wordWrap={true}
/>
</LegacyCard.Section>
</LegacyCard>
</Collapsible>
)}| </Collapsible> | ||
| )} | ||
| </VerticalStack> | ||
| ) |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid automated semicolon insertion (90% of all statements in the enclosing function have an explicit semicolon).
| ) | |
| ); |
No description provided.