Skip to content
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

[sarif] GH Scanning: Fingerprints & Array/Position Validation #5273

Merged
merged 1 commit into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ object SarifSchema {
* The portion of the artifact contents within the specified region.
*/
def snippet: Option[ArtifactContent]

/** @return
* true if startLine is empty and larger than 0, as this is the main required property.
*/
def isEmpty: Boolean = startLine.forall(_ <= 0)
}

/** Metadata that describes a specific report produced by the tool, as part of the analysis it provides or its runtime
Expand Down Expand Up @@ -219,6 +224,12 @@ object SarifSchema {
* An array of 'codeFlow' objects relevant to the result.
*/
def codeFlows: List[CodeFlow]

/** GitHub makes use of this property to track effectively the same finding across files between versions.
* @return
* A set of strings that contribute to the stable, unique identity of the result.
*/
def partialFingerprints: Map[String, String]
}

/** Describes a single run of an analysis tool, and contains the reported output of that run.
Expand Down Expand Up @@ -368,17 +379,30 @@ object SarifSchema {
}
)
),
new CustomSerializer[SarifSchema.PhysicalLocation](implicit format =>
(
{ case _ =>
???
},
{ case location: SarifSchema.PhysicalLocation =>
val elementMap = Map.newBuilder[String, Any]
elementMap.addOne("artifactLocation" -> location.artifactLocation)
if !location.region.isEmpty then elementMap.addOne("region" -> Extraction.decompose(location.region))
Extraction.decompose(elementMap.result())
}
)
),
new CustomSerializer[SarifSchema.Region](implicit format =>
(
{ case _ =>
???
},
{ case region: SarifSchema.Region =>
val elementMap = Map.newBuilder[String, Any]
region.startLine.foreach(x => elementMap.addOne("startLine" -> x))
region.startColumn.foreach(x => elementMap.addOne("startColumn" -> x))
region.endLine.foreach(x => elementMap.addOne("endLine" -> x))
region.endColumn.foreach(x => elementMap.addOne("endColumn" -> x))
region.startLine.filterNot(x => x <= 0).foreach(x => elementMap.addOne("startLine" -> x))
region.startColumn.filterNot(x => x <= 0).foreach(x => elementMap.addOne("startColumn" -> x))
region.endLine.filterNot(x => x <= 0).foreach(x => elementMap.addOne("endLine" -> x))
region.endColumn.filterNot(x => x <= 0).foreach(x => elementMap.addOne("endColumn" -> x))
region.snippet.foreach(x => elementMap.addOne("snippet" -> x))
Extraction.decompose(elementMap.result())
}
Expand All @@ -400,6 +424,29 @@ object SarifSchema {
}
)
),
new CustomSerializer[SarifSchema.Result](implicit format =>
(
{ case _ =>
???
},
{ case result: SarifSchema.Result =>
val elementMap = Map.newBuilder[String, Any]
elementMap.addOne("ruleId" -> result.ruleId)
elementMap.addOne("message" -> result.message)
elementMap.addOne("level" -> result.level)
// Locations & related locations have no minimum, but do not allow duplicates
elementMap.addOne("locations" -> result.locations.distinct)
elementMap.addOne("relatedLocations" -> result.relatedLocations.distinct)
// codeFlows may be empty, but thread flows may not have empty arrays
elementMap.addOne("codeFlows" -> result.codeFlows.filterNot(_.threadFlows.isEmpty))

if result.partialFingerprints.nonEmpty then
elementMap.addOne("partialFingerprints" -> result.partialFingerprints)

Extraction.decompose(elementMap.result())
}
)
),
new CustomSerializer[ToolComponent](implicit format =>
(
{ case _ =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ class JoernScanResultToSarifConverter extends ScanResultToSarifConverter {
}

protected def evidenceToCodeFlow(finding: Finding): Schema.CodeFlow = {
Schema.CodeFlow(threadFlows =
Schema.ThreadFlow(
finding.evidence.map(node => Schema.ThreadFlowLocation(location = nodeToLocation(node))).l
) :: Nil
)
val locations = finding.evidence.map(node => Schema.ThreadFlowLocation(location = nodeToLocation(node))).l
if (locations.isEmpty) {
Schema.CodeFlow(threadFlows = Nil)
} else {
Schema.CodeFlow(threadFlows = Schema.ThreadFlow(locations) :: Nil)
}
}

protected def createMessage(text: String): Schema.Message = {
Expand Down Expand Up @@ -88,7 +89,7 @@ class JoernScanResultToSarifConverter extends ScanResultToSarifConverter {
startColumn = n.columnNumber,
snippet = Option(Schema.ArtifactContent(n.code))
)
case _ => null
case _ => Schema.Region(None, None, None)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ object Schema {
level: String,
locations: List[Location],
relatedLocations: List[Location],
codeFlows: List[CodeFlow]
codeFlows: List[CodeFlow],
partialFingerprints: Map[String, String] = Map.empty
) extends SarifSchema.Result

final case class Run(tool: Tool, results: List[SarifSchema.Result], originalUriBaseIds: Map[String, ArtifactLocation])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,6 @@ class SarifTests extends AnyWordSpec with Matchers {
| },
| "results":[
| {
| "ruleId":"f1",
| "message":{
| "text":"Rule 1"
| },
| "level":"error",
| "locations":[
| {
| "physicalLocation":{
Expand Down Expand Up @@ -130,6 +125,9 @@ class SarifTests extends AnyWordSpec with Matchers {
| }
| }
| ],
| "message":{
| "text":"Rule 1"
| },
| "codeFlows":[
| {
| "threadFlows":[
Expand All @@ -155,7 +153,9 @@ class SarifTests extends AnyWordSpec with Matchers {
| }
| ]
| }
| ]
| ],
| "ruleId":"f1",
| "level":"error"
| }
| ],
| "originalUriBaseIds":{
Expand All @@ -166,6 +166,7 @@ class SarifTests extends AnyWordSpec with Matchers {
| }
| ]
|}
|
|""".stripMargin.trim
}

Expand Down Expand Up @@ -237,11 +238,6 @@ class SarifTests extends AnyWordSpec with Matchers {
| },
| "results":[
| {
| "ruleId":"f1",
| "message":{
| "text":"<empty>"
| },
| "level":"warning",
| "locations":[
| {
| "physicalLocation":{
Expand Down Expand Up @@ -272,6 +268,9 @@ class SarifTests extends AnyWordSpec with Matchers {
| }
| }
| ],
| "message":{
| "text":"<empty>"
| },
| "codeFlows":[
| {
| "threadFlows":[
Expand All @@ -296,7 +295,9 @@ class SarifTests extends AnyWordSpec with Matchers {
| }
| ]
| }
| ]
| ],
| "ruleId":"f1",
| "level":"warning"
| }
| ],
| "originalUriBaseIds":{
Expand Down
Loading