11import type { IResultManager } from '../../../engine/analyzer/common/result-manager'
22import type { IConfig } from '../../../config'
33import type { TaintFinding } from '../../../engine/analyzer/common/common-types'
4+ import type { SarifLocation } from '../../../engine/analyzer/common/sarif'
45
56const _ = require ( 'lodash' )
67const path = require ( 'path' )
@@ -16,8 +17,7 @@ const logger = require('../../../util/logger')(__filename)
1617const {
1718 registerDedupFunction,
1819} = require ( '../../../engine/analyzer/common/entrypoint/merge-coordinator' ) as typeof import ( '../../../engine/analyzer/common/entrypoint/merge-coordinator' )
19-
20- type SarifLocation = { physicalLocation ?: Record < string , unknown > ; [ key : string ] : unknown }
20+ const { getJavaFindingSignature } = require ( '../../taint/java/java-finding-candidate' ) as typeof import ( '../../taint/java/java-finding-candidate' )
2121
2222const {
2323 prepareLocation,
@@ -265,7 +265,7 @@ function extractSinkKey(finding: TracePreserveFinding): string | null {
265265}
266266
267267type JavaTraceGroup = { call : string ; argPass : string }
268- type JavaFindingProjection = { source : string ; sink : string ; groups : JavaTraceGroup [ ] }
268+ type JavaFindingProjection = { source : string ; sink : string ; sinkAttribute : string ; groups : JavaTraceGroup [ ] }
269269
270270function traceItemIdentity ( item : TaintTraceItem ) : string | null {
271271 const file = getTraceFileKey ( item )
@@ -282,6 +282,9 @@ function getJavaFindingProjection(finding: TracePreserveFinding): JavaFindingPro
282282 const source = extractSourceKey ( finding )
283283 const sink = extractSinkKey ( finding )
284284 if ( ! source || source . includes ( ':-1:-1' ) || ! sink || sink . includes ( ':-1:-1' ) ) return null
285+ // sinkAttribute 纳入投影键,避免不同 attribute 的同类 finding 被误去重
286+ const attrArr = ( finding as any ) . sinkAttribute as string [ ] | undefined
287+ const sinkAttribute = Array . isArray ( attrArr ) ? attrArr . slice ( ) . sort ( ) . join ( ',' ) : ''
285288 const groups : JavaTraceGroup [ ] = [ ]
286289 for ( let i = 0 ; i < finding . trace . length ; i ++ ) {
287290 const item = finding . trace [ i ]
@@ -293,12 +296,12 @@ function getJavaFindingProjection(finding: TracePreserveFinding): JavaFindingPro
293296 if ( call && argPass ) groups . push ( { call, argPass } )
294297 i ++
295298 }
296- return { source, sink, groups }
299+ return { source, sink, sinkAttribute , groups }
297300}
298301
299302function getJavaProjectionKey ( projection : JavaFindingProjection ) : string {
300303 const groups = projection . groups . map ( ( group ) => `${ group . call } ${ group . argPass } ` ) . join ( '' )
301- return `${ projection . source } ${ projection . sink } ${ groups } `
304+ return `${ projection . source } ${ projection . sink } ${ projection . sinkAttribute } ${ groups } `
302305}
303306
304307const javaProjectionIndexes = new WeakMap < TaintFinding [ ] , Map < string , TaintFinding > > ( )
@@ -316,15 +319,21 @@ function getJavaProjectionIndex(category: TaintFinding[]): Map<string, TaintFind
316319}
317320
318321function isDuplicateJavaFinding ( category : TaintFinding [ ] , finding : TaintFinding ) : boolean {
319- const projection = getJavaFindingProjection ( finding as TracePreserveFinding )
320- if ( ! projection ) return false
321- return getJavaProjectionIndex ( category ) . has ( getJavaProjectionKey ( projection ) )
322+ const signature = getJavaFindingSignature ( finding )
323+ if ( ! signature ) return false
324+ for ( const issue of category ) {
325+ if ( getJavaFindingSignature ( issue ) === signature ) return true
326+ }
327+ return false
322328}
323329
324330function rememberJavaFinding ( category : TaintFinding [ ] , finding : TaintFinding ) : void {
331+ const signature = getJavaFindingSignature ( finding )
332+ if ( ! signature ) return
333+ // 保持统一 finding 签名索引的建立副作用,实际查重使用同一 helper。
325334 const projection = getJavaFindingProjection ( finding as TracePreserveFinding )
326- if ( ! projection ) return
327- getJavaProjectionIndex ( category ) . set ( getJavaProjectionKey ( projection ) , finding )
335+ if ( projection ) getJavaProjectionIndex ( category ) . set ( getJavaProjectionKey ( projection ) , finding )
336+ void signature
328337}
329338
330339function invalidateJavaProjectionIndex ( category : TaintFinding [ ] ) : void {
@@ -383,7 +392,7 @@ class TaintOutputStrategy extends OutputStrategy {
383392 * @param config
384393 * @param printf
385394 */
386- outputFindings ( resultManager : IResultManager , outputFilePath : string , config : IConfig , printf : any ) : void {
395+ outputFindings ( resultManager : IResultManager , outputFilePath : string , config : IConfig , printf : any ) : any {
387396 const outputStartedAt = Date . now ( )
388397 let reportFilePath
389398 if ( resultManager ) {
@@ -425,8 +434,10 @@ class TaintOutputStrategy extends OutputStrategy {
425434 logger . info ( `[outputFindings] strategy=taintflow phase=write elapsed=${ Date . now ( ) - writeStartedAt } ms total=${ Date . now ( ) - outputStartedAt } ms` )
426435 // for taint flow checker, output result to console at the same time
427436 logger . info ( `report is write to ${ reportFilePath } ` )
437+ return results
428438 }
429439 }
440+ return undefined
430441 }
431442
432443 /**
@@ -439,14 +450,27 @@ class TaintOutputStrategy extends OutputStrategy {
439450 if ( ! finding ) return false
440451 let category : TaintFinding [ ] | undefined
441452 try {
442- category = resultManager ?. findings [ TaintOutputStrategy . outputStrategyId ] as TaintFinding [ ] | undefined
453+ // 跨入口 dedup:dedupIndex(已 snapshot 的历史 finding)+ findings(当前批次 live finding)
454+ const dedupCategory = resultManager ?. dedupIndex ?. [ TaintOutputStrategy . outputStrategyId ] as TaintFinding [ ] | undefined
455+ const currentCategory = resultManager ?. findings ?. [ TaintOutputStrategy . outputStrategyId ] as TaintFinding [ ] | undefined
456+ if ( ! dedupCategory && ! currentCategory ) return true
457+ if ( ! dedupCategory ) {
458+ category = currentCategory
459+ } else if ( ! currentCategory ) {
460+ category = dedupCategory
461+ } else {
462+ category = [ ...dedupCategory , ...currentCategory ]
463+ }
443464 if ( ! category ) return true
444465 if ( isDuplicateJavaFinding ( category as TaintFinding [ ] , finding ) ) return false
445466 // 依赖 trace 形态的折叠判据只比较原始链路节点,避免输出辅助节点改变去重结果。
446467 // 非 callstack-only 输出下 trace 通常不含 `_synthetic` 标记,过滤后与原数组等价。
447468 const findingTraceNoSynthetic = filterOutSyntheticSteps ( getDedupTrace ( finding ) )
448469 for ( let i = 0 ; i < category . length ; i ++ ) {
449470 const issue = category [ i ]
471+ const issueProjection = getJavaFindingProjection ( issue as TracePreserveFinding )
472+ const findingProjection = getJavaFindingProjection ( finding as TracePreserveFinding )
473+ if ( issueProjection && findingProjection && getJavaProjectionKey ( issueProjection ) !== getJavaProjectionKey ( findingProjection ) ) continue
450474 if (
451475 issue . line === finding . line &&
452476 isNodeEqual ( issue . node , finding . node ) &&
@@ -556,7 +580,7 @@ class TaintOutputStrategy extends OutputStrategy {
556580 uri ,
557581 snippetText ,
558582 nodeHash ,
559- affectedNodeName ,
583+ affectedNodeName
560584 ) ,
561585 key
562586 )
@@ -573,7 +597,7 @@ class TaintOutputStrategy extends OutputStrategy {
573597 'egg controller' ,
574598 item . str ,
575599 nodeHash ,
576- affectedNodeName ,
600+ affectedNodeName
577601 ) ,
578602 key
579603 )
@@ -605,7 +629,7 @@ class TaintOutputStrategy extends OutputStrategy {
605629 trace ,
606630 location ,
607631 finding . matchedSanitizerTags ,
608- callstackElements ,
632+ callstackElements
609633 )
610634 )
611635 } )
0 commit comments