Feature/scan findings window (AST-163510) - #261
Conversation
Extracted from other/scan-backend: the DevAssist scanner infrastructure (ASCA/OSS/IaC/Secrets/Containers scanners, project lifecycle detection, real-time editor scanning, problem/marker pipeline) and the Checkmarx Findings + Ignored Problems views that display detected issues, including gutter/underline editor annotations and the AI-Assist remediation action. Excludes the MCP-injection feature (devassist/configuration) and unrelated work from that branch (welcome dialog, promotional/preferences UI, login validation, dark theme). Also fixes a pre-existing build.properties/lib jackson version mismatch that blocked packaging from main. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Security Policy Alert: Actions Policy ViolationThis workflow run has been blocked by StepSecurity's actions policy. Disallowed Actions:
To fix this issue, please modify the workflow to use only allowed actions. Contact your organization administrator to request changes to the allowed actions list if needed. For more information, see StepSecurity's Actions Policy documentation. |
| Bundle-Version: 1.0.0.qualifier | ||
| Bundle-Vendor: Checkmarx | ||
| Require-Bundle: org.eclipse.ui, | ||
| org.eclipse.ui.workbench.texteditor, |
There was a problem hiding this comment.
Optional : do not need bundle-version ranges matching the minimum Eclipse platform this feature was built against?
| org.osgi.service.event;version="1.4.1" | ||
| Bundle-ActivationPolicy: lazy | ||
| Bundle-Activator: com.checkmarx.eclipse.Activator | ||
| Export-Package: com.checkmarx.ast.asca, |
There was a problem hiding this comment.
Optional : Export-Package re-exports a vendored third-party jar's internals as public API with no version. do we need to add an explicit version="2.4.24" attribute (bumped alongside every wrapper-jar upgrade)?
| // If file has unsaved changes, use current time to force re-scan | ||
| // This ensures edits are detected even if not yet saved to disk | ||
| if (hasUnsavedChanges) { | ||
| return System.nanoTime(); // Force different hash on every check while dirty |
There was a problem hiding this comment.
When the file is dirty, the method returns System.nanoTime() — different on every call regardless of whether any new edit occurred. Any caller that re-invokes scanFile() for reasons other than a fresh keystroke will trigger a full unnecessary rescan every time, contradicting the class's stated purpose.
Suggested fix: Hash actual document content (a checksum of the IDocument) for dirty files instead of a monotonic timestamp.
| * @param currentStateHash Current state of the file | ||
| * @return true if file changed (or never scanned), false if unchanged | ||
| */ | ||
| public boolean hasChanged(String filePath, long currentStateHash) { |
There was a problem hiding this comment.
Optional :
hasChanged()/updateStateHash() are independent calls with no shared lock. The same state holder is fetched by both RealTimeScanJob (per-keystroke) and the workspace-open scan path for the same file (e.g. an open, edited manifest/IaC file); both can observe "changed" concurrently and both launch a scan, duplicating backend work and racing on the result publish.
Suggested fix: Add an atomic in-flight marker (e.g. putIfAbsent on a ConcurrentHashMap<String,Boolean>) so a second concurrent trigger can detect the in-flight scan and skip/defer.
| return ANNOTATION_TYPE_MEDIUM; | ||
| } | ||
|
|
||
| switch (severity.toLowerCase()) { |
There was a problem hiding this comment.
"Malicious" severity is not mapped and falls back to the MEDIUM annotation type
| for (ScanIssue issue : issues) { | ||
| String severity = issue.getSeverity(); | ||
| if (severity != null) { | ||
| counts.put(severity, counts.getOrDefault(severity, 0L) + 1); |
There was a problem hiding this comment.
File-node problem counts are keyed by raw, unnormalized severity strings
calculateProblemCount groups by issue.getSeverity() verbatim. OSS/Container/ASCA/IaC paths normalize to Title Case, but the Secrets engine path assigns the raw scanner severity string with no normalization — a case mismatch would split counts for nominally-identical severities across two map keys, understating the file-tree badge count.
Suggested fix: Normalize the severity key (e.g. .toLowerCase()) before using it, or route through the shared DevAssistUtils.normalizeSeverity.
Evidence: counts.put(severity, counts.getOrDefault(severity, 0L) + 1) uses the raw string; Secrets path (scanIssue.setSeverity(secret.getSeverity())) has no normalization call unlike the other 4 engines.
| ImageDescriptor imageDescriptor = registry.getImageDescriptor(fileName); | ||
|
|
||
| if (imageDescriptor != null) { | ||
| Image image = imageDescriptor.createImage(); |
There was a problem hiding this comment.
File-icon Image created fresh — uncached and never disposed — on every tree refresh
getFileIcon() calls createImage() and returns it directly with no cache and no dispose(). This runs once per file on every setInput() call, which fires after every debounced real-time-scan completion — i.e., roughly once per second while a developer types. Each call allocates a new OS-level image handle that is never released: an unbounded native-handle leak over a working session, not just extra CPU.
Suggested fix: Cache the per-filename Image (e.g. a Map<String,Image>) and reuse it across calls; dispose only when the provider itself is disposed.
Evidence: Image image = imageDescriptor.createImage(); ... return image; — no cache lookup, no dispose; getElements() calls getFileIcon() per file on every invocation, and setInput() is invoked from the ISSUES_UPDATED_TOPIC handler on every scan update.
|
|
||
|
|
||
| // Calculate and log severity counts | ||
| java.util.Map<String, Long> counts = new java.util.HashMap<>(); |
There was a problem hiding this comment.
Dead severity-count computation allocates and iterates on every tree-node build with no consumer
A HashMap<String,Long> is built by iterating every issue for a file inside getElements()'s per-file lambda, but the result is never passed to FileNodeLabel or used anywhere — dead computation on a path already invoked after every real-time-scan edit cycle.
Suggested fix: Remove the unused computation.
Evidence: Map<String, Long> counts = new HashMap<>(); for (ScanIssue issue : issues) { ... } immediately followed by return new FileNodeLabel(fileName, entry.getKey(), issues, fileIcon); — counts never referenced again.
| @Override | ||
| protected void createButtonsForButtonBar(Composite parent) { | ||
| // Remove default OK/Cancel buttons, add Close button | ||
| createButton(parent, org.eclipse.jface.dialogs.IDialogConstants.CLOSE_ID, "Close", true); |
There was a problem hiding this comment.
The dialog's "Close" button does not close the dialog. A button with id CLOSE_ID is registered, but the class never overrides buttonPressed(int)
cx-anand-nandeshwar
left a comment
There was a problem hiding this comment.
Create separate module for devassist
PR Description
This PR introduces the initial implementation of DevAssist, focusing on the backend infrastructure required to detect runtime vulnerabilities.
The current implementation includes:
During the review and follow-up discussions, we identified several gaps and areas for improvement in the current implementation. Rather than expanding the scope of this PR, those enhancements will be addressed in a subsequent PR.
The next PR will primarily focus on UI-related improvements, including refining the user experience, aligning the implementation more closely with the intended workflow, and addressing the identified functional gaps.