-
Notifications
You must be signed in to change notification settings - Fork 42
Add diff logic and parallel logger support for audit #642
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: dev
Are you sure you want to change the base?
Conversation
…Results) - based on v1.24.2
- Add branchdiff.go with RunBranchDiffAudit function - Scans target branch first, then source branch (sequential) - SCA diff handled internally by CLI - JAS diff handled by CompareJasResults - Add MergeStatusCodes for partial results filtering - Status codes merged (worst of target + source) for frogbot filtering
- Add logger field to AuditBasicParams with getter/setter - RunAudit swaps global logger if custom logger provided - Enables frogbot to capture logs per-scan for ordered output
- Add LogCollector that captures logs in isolated buffer per audit - Enables parallel audits without log interleaving - Uses goroutine-local logger from jfrog-client-go - Propagate logger to child goroutines in JAS runner and SCA scan - Remove unused diff completion log message
attiasas
left a comment
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.
Nice work! checkout my comments, also:
- Update the PR description with the changes
- Adjust tests base on changes
commands/audit/logcollector.go
Outdated
| @@ -0,0 +1,41 @@ | |||
| package audit | |||
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.
maybe this should be at jfrog/jfrog-client-go#1297?
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.
these are the options
Move LogCollector to client-go - but it's really just a wrapper
Delete LogCollector entirely - Frogbot can use BufferedLogger directly from client-go
or we Keep it as is
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.
Removing code from repo is always good :)
If you already passing it in params, just use it no need for wrapper here
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.
doen tell me if you like it now
|
|
||
| // replace github.com/jfrog/froggit-go => github.com/jfrog/froggit-go master | ||
|
|
||
| replace github.com/jfrog/jfrog-client-go => github.com/eyalk007/jfrog-client-go v0.0.0-20260114112951-67b77f49255f |
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.
Reminder to remove replace after merging dependend PR
utils/results/diff.go
Outdated
|
|
||
| // CompareJasResults computes the diff between target and source JAS results. | ||
| // Returns only NEW findings in source that don't exist in target. | ||
| func CompareJasResults(targetResults, sourceResults *SecurityCommandResults) *SecurityCommandResults { |
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.
IDK about the func name, it does not compare, it filters Source results that exists at target
utils/results/diff.go
Outdated
| func extractLocationsOnly(run *sarif.Run, targetKeys map[string]bool) { | ||
| for _, result := range run.Results { | ||
| for _, location := range result.Locations { | ||
| key := getRelativeLocationFileName(location, run.Invocations) + getLocationSnippetText(location) |
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.
what about file name changes? it will not catch this...
why are we first filtering only locations and than by fingerprints, can we do it in one go? (reducing go over results multile times)
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.
For SAST: Uses fingerprints (precise_sink_and_sink_function) which are content-based, so renames don't matter
for secrets and iac -
This is a known limitation - AM doesn't handle file renames either. If you rename a file, existing findings in that file will show as "new" in the diff. This is acceptable
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.
We can write a todo and create a ticket for it, but i dont believe we should handle it now
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.
yeah, we should
commands/audit/logcollector.go
Outdated
| @@ -0,0 +1,41 @@ | |||
| package audit | |||
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.
Removing code from repo is always good :)
If you already passing it in params, just use it no need for wrapper here
commands/audit/audit.go
Outdated
| // Worker goroutines need this propagated so their logs are captured in the same buffer. | ||
| currentLogger := log.GetLogger() | ||
| jasTask := createJasScansTask(auditParallelRunner, scanResults, serverDetails, auditParams, jasScanner) | ||
| wrappedJasTask := func(threadId int) error { | ||
| // Propagate parent's logger to this worker goroutine for isolated log capture | ||
| log.SetLoggerForGoroutine(currentLogger) | ||
| defer log.ClearLoggerForGoroutine() | ||
| return jasTask(threadId) | ||
| } | ||
| if _, jasErr := auditParallelRunner.Runner.AddTaskWithError(wrappedJasTask, func(taskErr error) { |
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.
I think you can use WrapTaskWithLoggerPropagation here as well
| log.Info("[DIFF] New findings after diff - Secrets:", diffSecrets, "| IaC:", diffIac, "| SAST:", diffSast) | ||
| log.Info("[DIFF] Filtered out - Secrets:", sourceSecrets-diffSecrets, "| IaC:", sourceIac-diffIac, "| SAST:", sourceSast-diffSast) |
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.
| log.Info("[DIFF] New findings after diff - Secrets:", diffSecrets, "| IaC:", diffIac, "| SAST:", diffSast) | |
| log.Info("[DIFF] Filtered out - Secrets:", sourceSecrets-diffSecrets, "| IaC:", sourceIac-diffIac, "| SAST:", sourceSast-diffSast) | |
| log.Debug("[DIFF] New findings after diff - Secrets:", diffSecrets, "| IaC:", diffIac, "| SAST:", diffSast) | |
| log.Debug("[DIFF] Filtered out - Secrets:", sourceSecrets-diffSecrets, "| IaC:", sourceIac-diffIac, "| SAST:", sourceSast-diffSast) |
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.
i acutally think they are good info logs
its for hte userr to undestand the state post diff, im waiting on or to tell me what she thinks on logs @orto17
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.
talked to or she wants them as info
| // CompareJasResults computes the diff between target and source JAS results. | ||
| // Returns only NEW findings in source that don't exist in target. | ||
| func CompareJasResults(targetResults, sourceResults *SecurityCommandResults) *SecurityCommandResults { | ||
| log.Info("[DIFF] Starting JAS diff calculation") |
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.
| log.Info("[DIFF] Starting JAS diff calculation") | |
| log.Debug("[DIFF] Starting JAS diff calculation") |
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.
dont know if i agrree with it, i think it should be info, we can maybe do it in frogbot and log it info there
@orto17
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.
talked to or she wants them on info
attiasas
left a comment
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 we are moving diff logic here, do we still need the logic in:
jas/sast/jas/secrets/jas/iac?
talking about:
log.Debug("Diff mode - SAST results to compare provided")
manager.resultsToCompareFileName = filepath.Join(scannerTempDir, "target.sarif")
// Save the sast results to compare as a report
err = jas.SaveScanResultsToCompareAsReport(manager.resultsToCompareFileName, resultsToCompare...)
make sure if not needed to delete related logic
- Rename filterExistingFindings to excludeExistingFindingsInTargets - Extract countJasFindings and extractAllJasResultKeys helpers - Move getSastFingerprint to sarifutils.GetSastDiffFingerprint - Use WrapTaskWithLoggerPropagation helper in audit.go - Remove LogCollector wrapper - use BufferedLogger directly from client-go
When merging SCA and JAS diff results, the ResultsStatus field was not being copied, causing all status codes (SastStatusCode, IacStatusCode, etc.) to remain nil. This resulted in Frogbot displaying 'Not Scanned' for JAS scans even when they ran successfully but produced 0 new findings after diff. Now properly merging SCA status codes (including ContextualAnalysis) and JAS-only status codes (SAST, Secrets, IaC) into the unified results.
5e7efdc to
bccd932
Compare
When computing JAS diff, the ResultsStatus was not being copied to the diff results, causing all JAS scan status codes (SAST, Secrets, IaC) to be nil. This led to 'Not Scanned' being displayed in Frogbot PR comments even when scans ran successfully. Now copying ResultsStatus from source to diff results so status codes are preserved and merged correctly.
- Rename CompareJasResults → FilterNewJasFindings (more accurate name) - Rename test functions: TestFilterSarifRuns_* → TestFilterNewSarifFindings_* - Update test calls to use variadic signatures for extractLocationsOnly/extractFingerprints - Use sarifutils.GetSastDiffFingerprint instead of local getSastFingerprint Addresses code review feedback about misleading function names.
attiasas
left a comment
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.
Nice! take a look at my comments
| if collector := auditParams.GetLogCollector(); collector != nil { | ||
| log.SetLoggerForGoroutine(collector) | ||
| defer log.ClearLoggerForGoroutine() | ||
| } |
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 collector is running on main routine so it will first record all the logs from main and it will collect the other after?
| // Wrap task to propagate parent's logger for isolated parallel logging | ||
| wrappedJasTask := utils.WrapTaskWithLoggerPropagation(jasTask) | ||
| if _, jasErr := auditParallelRunner.Runner.AddTaskWithError(wrappedJasTask, func(taskErr error) { |
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.
we only want to wrap and change logs if collector is set.
why are we always wrapping?
| // SastDiffFingerprintKey is the fingerprint key used by Analyzer Manager for SAST diff matching. | ||
| // This differs from jasutils.SastFingerprintKey ("significant_full_path") which is used for general SAST operations. | ||
| const SastDiffFingerprintKey = "precise_sink_and_sink_function" |
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.
lets move the const to jasutils to be near the other used fingerprint value (easy to maintain and see what we use)
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 you can also add to GetResultFingerprint check for result nil case as well. just saw its missing
| filteredSbom := cdxutils.Exclude(*sbom, *params.TargetResultToCompare.ScaResults.Sbom.Components...) | ||
| return filteredSbom |
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.
| filteredSbom := cdxutils.Exclude(*sbom, *params.TargetResultToCompare.ScaResults.Sbom.Components...) | |
| return filteredSbom | |
| return cdxutils.Exclude(*sbom, *params.TargetResultToCompare.ScaResults.Sbom.Components...) |
why change is needed?
|
|
||
| sourceSecrets, sourceIac, sourceSast := countJasFindings(sourceJasResults) | ||
|
|
||
| log.Debug("[DIFF] Source findings before diff - Secrets:", sourceSecrets, "| IaC:", sourceIac, "| SAST:", sourceSast) |
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.
maybe you missed that, info as well?
| count := 0 | ||
| for _, run := range runs { | ||
| if run != nil { | ||
| count += len(run.Results) |
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.
I don't want to count on JAS, the correct impl should be the SARIF spec.
They have to follow it, so we should do the same.
| count := 0 | ||
| for _, run := range runs { | ||
| if run != nil { | ||
| count += len(run.Results) |
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.
at the end, the CLI result parser to format goes over each location as diff issue
take a look at ForEachJasIssue
| func strPtr(s string) *string { | ||
| return &s | ||
| } |
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.
remove and use utils.NewStringPtr() instead
| // Note: Tests for extractRelativePath, getLocationSnippetText, getLocationFileName, and | ||
| // getInvocationWorkingDirectory have been removed as these now use sarifutils functions. | ||
|
|
||
| func TestGetSastFingerprint(t *testing.T) { |
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.
move to sarifutils_test.go
devbranch.go vet ./....go fmt ./....Depends on: