Skip to content
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 @@ -11,7 +11,7 @@ import io.joern.x2cpg.ValidationMode
import io.joern.x2cpg.X2Cpg.newEmptyCpg
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.utils.FileUtil
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

trait AstC2CpgFrontend extends LanguageFrontend {
override type ConfigType = Config
Expand Down Expand Up @@ -49,7 +49,7 @@ trait AstC2CpgFrontend extends LanguageFrontend {
)
headerPass.createAndApply()
new FunctionDeclNodePass(cpg, headerPass.unhandledMethodDeclarations(), config).createAndApply()
new PostFrontendValidator(cpg, false).run()
new PostFrontendValidator(cpg, ValidationLevel.V0).run()
cpg
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import io.joern.c2cpg.Config
import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.utils.FileUtil
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

trait C2CpgFrontend extends LanguageFrontend {
override type ConfigType = Config
Expand All @@ -16,7 +16,7 @@ trait C2CpgFrontend extends LanguageFrontend {
.fold(Config())(_.asInstanceOf[Config])
.withInputPath(sourceCodePath.getAbsolutePath)
val res = c2cpg.createCpg(config).get
new PostFrontendValidator(res, false).run()
new PostFrontendValidator(res, ValidationLevel.V0).run()
res
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.utils.FileUtil
import io.shiftleft.semanticcpg.language.{ICallResolver, NoResolve}
import io.shiftleft.semanticcpg.layers.LayerCreatorContext
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}
import org.scalatest.Inside

import java.io.File
Expand Down Expand Up @@ -87,7 +87,7 @@ trait CSharpFrontend extends LanguageFrontend {
override def execute(sourceCodeFile: File): Cpg = {
val config = defaultConfig.withInputPath(sourceCodeFile.getAbsolutePath)
val tmp = new CSharpSrc2Cpg().createCpg(config).get
new PostFrontendValidator(tmp, false).createAndApply()
new PostFrontendValidator(tmp, ValidationLevel.V0).createAndApply()
tmp
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.joern.ghidra2cpg.fixtures
import io.joern.x2cpg.X2Cpg.applyDefaultOverlays
import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}
import io.shiftleft.utils.ProjectRoot
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
Expand All @@ -24,7 +24,7 @@ class BinToCpgFixture(val frontend: LanguageFrontend) extends AnyWordSpec with M
val bin = new File(binDirectory, binName)
cpg = frontend.execute(bin)
passes(cpg)
new PostFrontendValidator(cpg, false).run()
new PostFrontendValidator(cpg, ValidationLevel.V0).run()
}

override def afterAll(): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.apache.commons.io.FileUtils
import io.shiftleft.codepropertygraph.generated.nodes
import io.joern.dataflowengineoss.language.*
import io.shiftleft.semanticcpg.language.*
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

import java.nio.file.Files

Expand All @@ -30,7 +30,7 @@ class GhidraFrontend extends LanguageFrontend {
val cpgBin = dir.getAbsolutePath
val config = Config().withInputPath(inputFile.getAbsolutePath).withOutputPath(cpgBin)
val cpg = new Ghidra2Cpg().createCpg(config).get
new PostFrontendValidator(cpg, false).run()
new PostFrontendValidator(cpg, ValidationLevel.V0).run()
cpg
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import io.joern.x2cpg.testfixtures.{Code2CpgFixture, DefaultTestCpg}
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.language.{ICallResolver, NoResolve}
import io.shiftleft.semanticcpg.utils.FileUtil
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}
import org.scalatest.Inside
class DefaultTestCpgWithGo(val fileSuffix: String) extends DefaultTestCpg with SemanticTestCpg {
override type ConfigType = Config
Expand Down Expand Up @@ -41,7 +41,7 @@ class DefaultTestCpgWithGo(val fileSuffix: String) extends DefaultTestCpg with S
.getOrElse(Config())
.withInputPath(sourceCodePath.getAbsolutePath)
val res = goSrc2Cpg.get.createCpg(config).get
new PostFrontendValidator(res, false).run()
new PostFrontendValidator(res, ValidationLevel.V0).run()
res
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.codepropertygraph.generated.nodes.{Expression, Literal}
import io.shiftleft.semanticcpg.language.*
import io.shiftleft.semanticcpg.utils.FileUtil
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

import java.io.File
import java.nio.file.Path as JavaPath
Expand All @@ -37,7 +37,7 @@ trait JavaSrcFrontend extends LanguageFrontend {
.getOrElse(JavaSrc2Cpg.DefaultConfig.withDelombokMode("no-delombok"))
.withCacheJdkTypeSolver(true)
val res = new JavaSrc2Cpg().createCpg(config.withInputPath(sourceCodeFile.getAbsolutePath)).get
new PostFrontendValidator(res, false).run()
new PostFrontendValidator(res, ValidationLevel.V0).run()
res
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.joern.x2cpg.X2Cpg
import io.joern.x2cpg.testfixtures.{Code2CpgFixture, DefaultTestCpg, LanguageFrontend, TestCpg}
import io.shiftleft.semanticcpg.utils.FileUtil.*
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

import java.io.File
import java.nio.file.Path
Expand All @@ -24,7 +24,7 @@ trait Jimple2CpgFrontend extends LanguageFrontend {
override def execute(sourceCodeFile: File): Cpg = {
val config = getConfig().getOrElse(Config())
val cpg = new Jimple2Cpg().createCpg(config.withInputPath(sourceCodeFile.getAbsolutePath)).get
new PostFrontendValidator(cpg, false).run()
new PostFrontendValidator(cpg, ValidationLevel.V0).run()
cpg
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.joern.jssrc2cpg.testfixtures
import io.joern.jssrc2cpg.{Config, JsSrc2Cpg}
import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

trait JsSrc2CpgFrontend extends LanguageFrontend {
override type ConfigType = Config
Expand All @@ -12,7 +12,7 @@ trait JsSrc2CpgFrontend extends LanguageFrontend {
val jssrc2cpg = new JsSrc2Cpg()
val config = getConfig().getOrElse(Config(tsTypes = false)).withInputPath(sourceCodePath.getAbsolutePath)
val res = jssrc2cpg.createCpg(config).get
new PostFrontendValidator(res, true).run()
new PostFrontendValidator(res, ValidationLevel.V1).run()
res
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import io.joern.x2cpg.testfixtures.DefaultTestCpg
import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.semanticcpg.utils.FileUtil.*
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}
import io.shiftleft.utils.ProjectRoot

import java.io.File
Expand Down Expand Up @@ -41,7 +41,7 @@ trait KotlinFrontend extends LanguageFrontend {
}

val tmp = new Kotlin2Cpg().createCpg(config.withInputPath(sourceCodeFile.getAbsolutePath)).get
new PostFrontendValidator(tmp, true).run()
new PostFrontendValidator(tmp, ValidationLevel.V1).run()
tmp
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.joern.x2cpg.frontendspecific.php2cpg
import io.joern.x2cpg.testfixtures.{Code2CpgFixture, DefaultTestCpg, LanguageFrontend}
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.language.{ICallResolver, NoResolve}
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

import java.io.File

Expand All @@ -20,7 +20,7 @@ trait PhpFrontend extends LanguageFrontend {
override def execute(sourceCodeFile: File): Cpg = {
val defaultConfig: Config = getConfig().getOrElse(Config())
val cpg = new Php2Cpg().createCpg(defaultConfig.withInputPath(sourceCodeFile.getAbsolutePath)).get
new PostFrontendValidator(cpg, true).run()
new PostFrontendValidator(cpg, ValidationLevel.V1).run()
cpg
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.language.ICallResolver
import io.shiftleft.semanticcpg.language.NoResolve
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

trait PythonFrontend extends LanguageFrontend {
override type ConfigType = Py2CpgOnFileSystemConfig
Expand All @@ -41,7 +41,7 @@ trait PythonFrontend extends LanguageFrontend {
.withInputPath(sourceCodePath.getAbsolutePath)
)
.get
new PostFrontendValidator(tmp, false).run()
new PostFrontendValidator(tmp, ValidationLevel.V0).run()
tmp
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.joern.x2cpg.ValidationMode
import io.joern.x2cpg.testfixtures.*
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.language.{ICallResolver, NoResolve}
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}
import org.scalatest.Inside

import java.io.File
Expand All @@ -28,7 +28,7 @@ trait RubyFrontend(withDownloadDependencies: Boolean, disableFileContent: Boolea

override def execute(sourceCodeFile: File): Cpg = {
val tmp = Using.resource(new RubySrc2Cpg())(_.createCpg(config.withInputPath(sourceCodeFile.getAbsolutePath)).get)
new PostFrontendValidator(tmp, false).run()
new PostFrontendValidator(tmp, ValidationLevel.V0).run()
tmp
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.joern.swiftsrc2cpg.testfixtures
import io.joern.swiftsrc2cpg.{Config, SwiftSrc2Cpg}
import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

trait SwiftCompilerSrc2CpgFrontend extends LanguageFrontend {
final override type ConfigType = Config
Expand All @@ -12,7 +12,7 @@ trait SwiftCompilerSrc2CpgFrontend extends LanguageFrontend {
val pathAsString = sourceCodePath.toPath.resolve("SwiftTest").toAbsolutePath.toString
val config = getConfig().getOrElse(Config()).withInputPath(pathAsString)
val cpg = new SwiftSrc2Cpg().createCpg(config).get
new PostFrontendValidator(cpg, true).run()
new PostFrontendValidator(cpg, ValidationLevel.V1).run()
cpg
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.joern.swiftsrc2cpg.testfixtures
import io.joern.swiftsrc2cpg.{Config, SwiftSrc2Cpg}
import io.joern.x2cpg.testfixtures.LanguageFrontend
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.validation.PostFrontendValidator
import io.shiftleft.semanticcpg.validation.{PostFrontendValidator, ValidationLevel}

trait SwiftSrc2CpgFrontend extends LanguageFrontend {
override type ConfigType = Config
Expand All @@ -12,7 +12,7 @@ trait SwiftSrc2CpgFrontend extends LanguageFrontend {
val pathAsString = sourceCodePath.getAbsolutePath
val config = getConfig().getOrElse(Config()).withInputPath(pathAsString)
val tmp = new SwiftSrc2Cpg().createCpg(config).get
new PostFrontendValidator(tmp, true).run()
new PostFrontendValidator(tmp, ValidationLevel.V1).run()
tmp
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ import org.slf4j.{Logger, LoggerFactory}

class ValidationError(msg: String) extends RuntimeException(msg) {}

enum ValidationLevel(val value: Int) extends Ordered[ValidationLevel] {
case V0 extends ValidationLevel(0)
case V1 extends ValidationLevel(1)

override def compare(that: ValidationLevel): Int = this.value.compareTo(that.value)
}

/** Base validator pass that collects validation violations during graph traversal.
*
* @param cpg
Expand All @@ -26,16 +33,16 @@ abstract class AbstractValidator(cpg: Cpg) extends CpgPass(cpg) {
}

/** @return
* compressed violations as (message, count) pairs in stable key order
* compressed violations as (key, message, count) tuples in stable key order
*/
def getViolationsCompressed: mutable.ArrayBuffer[(String, Int)] = {
def getViolationsCompressedWithKey: mutable.ArrayBuffer[(Any, String, Int)] = {
mutable.ArrayBuffer.from(
violationsBuffer.iterator
.groupByStable(_._1)
.iterator
.map { (_, violations) => (violations.head._2, violations.size) }
.map { case (key, violations) => (key, violations.head._2, violations.size) }
.toSeq
.sortBy(_._1)
.sortBy(_._1.toString)
)
}
}
Expand All @@ -44,9 +51,17 @@ abstract class AbstractValidator(cpg: Cpg) extends CpgPass(cpg) {
object PostFrontendValidator {
private val logger: Logger = LoggerFactory.getLogger(getClass)

enum ErrorType {
case FULLNAME_UNIQUE_METHOD, FULLNAME_UNIQUE_TYPE, FULLNAME_UNIQUE_TYPEDECL, MULTI_REF, BAD_REF_TYPE, NONLOCAL_REF,
MULTI_AST_IN, MULTI_ARG_IN, DUPLICATE_ORDER
/** Newly added error types should have an `addedInLevel` one level higher than the highest */
enum ErrorType(val addedInLevel: ValidationLevel) {
case FULLNAME_UNIQUE_METHOD extends ErrorType(ValidationLevel.V1)
case FULLNAME_UNIQUE_TYPE extends ErrorType(ValidationLevel.V1)
case FULLNAME_UNIQUE_TYPEDECL extends ErrorType(ValidationLevel.V1)
case MULTI_REF extends ErrorType(ValidationLevel.V1)
case BAD_REF_TYPE extends ErrorType(ValidationLevel.V1)
case NONLOCAL_REF extends ErrorType(ValidationLevel.V1)
case MULTI_AST_IN extends ErrorType(ValidationLevel.V1)
case MULTI_ARG_IN extends ErrorType(ValidationLevel.V1)
case DUPLICATE_ORDER extends ErrorType(ValidationLevel.V1)
}
}

Expand All @@ -55,8 +70,11 @@ object PostFrontendValidator {
*
* Goal is to improve the pass once we fixed current violations, and then have new ideas what to check. Then plug in
* faster checking code, then enable in sptests and prod.
*
* NOTE: All validation checks with a level lower or equal to [[fatalValidationLevel]] will result in an exception. See
* [[ValidationLevel]] and [[ErrorType]] for the highest validation level.
*/
class PostFrontendValidator(cpg: Cpg, throwOnError: Boolean) extends AbstractValidator(cpg) {
class PostFrontendValidator(cpg: Cpg, fatalValidationLevel: ValidationLevel) extends AbstractValidator(cpg) {
import PostFrontendValidator.logger
import PostFrontendValidator.ErrorType.*

Expand Down Expand Up @@ -221,11 +239,22 @@ class PostFrontendValidator(cpg: Cpg, throwOnError: Boolean) extends AbstractVal

/** Log compressed violations and optionally throw on error. */
private def reportViolations(): Unit = {
val violations = getViolationsCompressed
val violations = getViolationsCompressedWithKey
if (violations.nonEmpty) {
val err = violations.map { (msg, n) => s"$msg ($n instances)" }.mkString("\n")
val err = violations.map { (_, msg, n) => s"$msg ($n instances)" }.mkString("\n")
logger.warn(s"Graph validation failure:\n$err")
if (throwOnError) throw new ValidationError(err)

val fatalViolations = violations.filter { case (key, _, _) =>
key match {
case err: ErrorType => err.addedInLevel <= fatalValidationLevel
case _ => true
}
}

if (fatalViolations.nonEmpty) {
val fatalErr = fatalViolations.map { case (_, msg, n) => s"$msg ($n instances)" }.mkString("\n")
throw new ValidationError(s"Fatal graph validation errors (Level <= $fatalValidationLevel):\n$fatalErr")
}
}
}

Expand Down
Loading