diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 2498d76..56d90f0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -78,3 +78,9 @@ jobs: env: JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8" shell: bash + - name: Generate SBOM with cdxgen + run: | + npm install -g @cyclonedx/cdxgen + cdxgen -t sbt -o bom.json . -p --no-recurse + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e3908d3..df08dba 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -101,11 +101,18 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_USERNAME: ${{ github.actor }} + - name: Generate SBOM with cdxgen + run: | + npm install -g @cyclonedx/cdxgen + cdxgen -t sbt -o bom.json . -p --no-recurse + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create Release if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@v1 with: files: | + bom.json target/atom.zip target/atom.zip.sha512 target/graalvm-native-image/atom-amd64 diff --git a/.github/workflows/repotests.yml b/.github/workflows/repotests.yml index f3264fc..5631340 100644 --- a/.github/workflows/repotests.yml +++ b/.github/workflows/repotests.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - java-version: ['21', '22'] + java-version: ['21', '22', '23'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 diff --git a/build.sbt b/build.sbt index 141dc15..0d748a1 100644 --- a/build.sbt +++ b/build.sbt @@ -1,34 +1,40 @@ name := "atom" ThisBuild / organization := "io.appthreat" -ThisBuild / version := "2.0.18" +ThisBuild / version := "2.0.19" ThisBuild / scalaVersion := "3.5.0" -val chenVersion = "2.1.5" +val chenVersion = "2.1.6" lazy val atom = Projects.atom val astGenVersion = "3.5.0" libraryDependencies ++= Seq( - "com.github.pathikrit" %% "better-files" % "3.9.2", - "com.github.scopt" %% "scopt" % "4.1.0", - "org.slf4j" % "slf4j-nop" % "2.0.16" % Optional, - "io.appthreat" %% "c2cpg" % Versions.chen excludeAll ( + "com.github.pathikrit" %% "better-files" % "3.9.2", + "com.github.scopt" %% "scopt" % "4.1.0", + "org.slf4j" % "slf4j-nop" % "2.0.16" % Optional, + ("io.appthreat" %% "c2cpg" % Versions.chen).excludeAll( ExclusionRule(organization = "com.ibm.icu", name = "icu4j"), ExclusionRule(organization = "org.jline", name = "jline"), ExclusionRule(organization = "org.eclipse.platform", name = "org.eclipse.jface"), ExclusionRule(organization = "org.eclipse.platform", name = "org.eclipse.jface.text") ), - "io.appthreat" %% "dataflowengineoss" % Versions.chen, - "io.appthreat" %% "pysrc2cpg" % Versions.chen, - "io.appthreat" %% "javasrc2cpg" % Versions.chen, - "io.appthreat" %% "jssrc2cpg" % Versions.chen, - "io.appthreat" %% "jimple2cpg" % Versions.chen, - "io.appthreat" %% "php2atom" % Versions.chen, - "io.appthreat" %% "semanticcpg" % Versions.chen % Test classifier "tests", - "io.appthreat" %% "x2cpg" % Versions.chen % Test classifier "tests", - "io.appthreat" %% "pysrc2cpg" % Versions.chen % Test classifier "tests", - "org.scalatest" %% "scalatest" % "3.2.19" % Test + "io.appthreat" %% "dataflowengineoss" % Versions.chen, + "io.appthreat" %% "pysrc2cpg" % Versions.chen, + "io.appthreat" %% "javasrc2cpg" % Versions.chen, + "io.appthreat" %% "jssrc2cpg" % Versions.chen, + "io.appthreat" %% "jimple2cpg" % Versions.chen, + "io.appthreat" %% "php2atom" % Versions.chen, + ("io.appthreat" %% "semanticcpg" % Versions.chen % Test).classifier("tests"), + ("io.appthreat" %% "x2cpg" % Versions.chen % Test).classifier("tests"), + ("io.appthreat" %% "pysrc2cpg" % Versions.chen % Test).classifier("tests"), + "org.scalatest" %% "scalatest" % "3.2.19" % Test +) + +excludeDependencies ++= Seq( + ExclusionRule("dev.scalapy", "scalapy-core"), + ExclusionRule("org.gradle", "gradle-tooling-api"), + ExclusionRule("org.scala-lang", "scala3-compiler") ) Compile / doc / scalacOptions ++= Seq("-doc-title", "atom apidocs", "-doc-version", version.value) @@ -43,16 +49,22 @@ ThisBuild / compile / javacOptions ++= Seq( "-Xlint", "--release=21" ) ++ { - // fail early if users with JDK11 try to run this - val javaVersion = sys.props("java.specification.version").toFloat - assert(javaVersion.toInt >= 21, s"this build requires JDK21+ - you're using $javaVersion") - Nil + // fail early if users with JDK11 try to run this + val javaVersion = sys.props("java.specification.version").toFloat + assert(javaVersion.toInt >= 21, s"this build requires JDK21+ - you're using $javaVersion") + Nil } Universal / topLevelDirectory := None Universal / mappings := (Universal / mappings).value.filter { - case (_, path) => !path.contains("org.scala-lang.scala3-compiler") && !path.contains("io.get-coursier") && !path.contains("com.michaelpollmeier.scala-repl-pp") + case (_, path) => !path.contains("org.scala-lang.scala3-compiler") && !path.contains( + "io.get-coursier" + ) && !path.contains("com.michaelpollmeier.scala-repl-pp") && !path.contains( + "dev.scalapy.scalapy-core" + ) && !path.contains( + "dev.scalapy.scalapy-macros" + ) } enablePlugins(JavaAppPackaging, ClasspathJarPlugin, GraalVMNativeImagePlugin) @@ -68,58 +80,58 @@ astGenDlUrl := s"https://github.com/joernio/astgen/releases/download/v${astGenVe lazy val astGenBinaryNames = taskKey[Seq[String]]("astgen binary names") astGenBinaryNames := { - if (sys.props.get("ALL_PLATFORMS").contains("TRUE")) { - Seq(AstgenWin, AstgenLinux, AstgenMac, AstgenMacArm) - } else { - Environment.operatingSystem match { - case Environment.OperatingSystemType.Windows => - Seq(AstgenWin) - case Environment.OperatingSystemType.Linux => - Environment.architecture match { - case Environment.ArchitectureType.X86 => Seq(AstgenLinux) - case Environment.ArchitectureType.ARM => Seq(AstgenLinuxArm) - } - case Environment.OperatingSystemType.Mac => - Environment.architecture match { - case Environment.ArchitectureType.X86 => Seq(AstgenMac) - case Environment.ArchitectureType.ARM => Seq(AstgenMacArm) - } - case Environment.OperatingSystemType.Unknown => + if (sys.props.get("ALL_PLATFORMS").contains("TRUE")) { Seq(AstgenWin, AstgenLinux, AstgenMac, AstgenMacArm) + } else { + Environment.operatingSystem match { + case Environment.OperatingSystemType.Windows => + Seq(AstgenWin) + case Environment.OperatingSystemType.Linux => + Environment.architecture match { + case Environment.ArchitectureType.X86 => Seq(AstgenLinux) + case Environment.ArchitectureType.ARM => Seq(AstgenLinuxArm) + } + case Environment.OperatingSystemType.Mac => + Environment.architecture match { + case Environment.ArchitectureType.X86 => Seq(AstgenMac) + case Environment.ArchitectureType.ARM => Seq(AstgenMacArm) + } + case Environment.OperatingSystemType.Unknown => + Seq(AstgenWin, AstgenLinux, AstgenMac, AstgenMacArm) + } } - } } lazy val astGenDlTask = taskKey[Unit](s"Download astgen binaries") astGenDlTask := { - val astGenDir = baseDirectory.value / "bin" / "astgen" - astGenDir.mkdirs() - - astGenBinaryNames.value.foreach { fileName => - val dest = astGenDir / fileName - if (!dest.exists) { - val url = s"${astGenDlUrl.value}$fileName" - val downloadedFile = SimpleCache.downloadMaybe(url) - IO.copyFile(downloadedFile, dest) + val astGenDir = baseDirectory.value / "bin" / "astgen" + astGenDir.mkdirs() + + astGenBinaryNames.value.foreach { fileName => + val dest = astGenDir / fileName + if (!dest.exists) { + val url = s"${astGenDlUrl.value}$fileName" + val downloadedFile = SimpleCache.downloadMaybe(url) + IO.copyFile(downloadedFile, dest) + } } - } - val distDir = (Universal / stagingDirectory).value / "bin" / "astgen" - distDir.mkdirs() - IO.copyDirectory(astGenDir, distDir) + val distDir = (Universal / stagingDirectory).value / "bin" / "astgen" + distDir.mkdirs() + IO.copyDirectory(astGenDir, distDir) - // permissions are lost during the download; need to set them manually - astGenDir.listFiles().foreach(_.setExecutable(true, false)) - distDir.listFiles().foreach(_.setExecutable(true, false)) + // permissions are lost during the download; need to set them manually + astGenDir.listFiles().foreach(_.setExecutable(true, false)) + distDir.listFiles().foreach(_.setExecutable(true, false)) } lazy val astGenSetAllPlatforms = taskKey[Unit](s"Set ALL_PLATFORMS") astGenSetAllPlatforms := { System.setProperty("ALL_PLATFORMS", "TRUE") } stage := Def - .sequential(astGenSetAllPlatforms, Universal / stage) - .andFinally(System.setProperty("ALL_PLATFORMS", "FALSE")) - .value + .sequential(astGenSetAllPlatforms, Universal / stage) + .andFinally(System.setProperty("ALL_PLATFORMS", "FALSE")) + .value // Also remove astgen binaries with clean, e.g., to allow for updating them. // Sadly, we can't define the bin/ folders globally, @@ -127,38 +139,40 @@ stage := Def cleanFiles ++= Seq( baseDirectory.value / "bin" / "astgen", (Universal / stagingDirectory).value / "bin" / "astgen" -) ++ astGenBinaryNames.value.map(fileName => SimpleCache.encodeFile(s"${astGenDlUrl.value}$fileName")) +) ++ astGenBinaryNames.value.map(fileName => + SimpleCache.encodeFile(s"${astGenDlUrl.value}$fileName") +) ThisBuild / licenses := List("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")) Global / onChangedBuildSource := ReloadOnSourceChanges -maintainer := "Team AppThreat " +maintainer := "Team AppThreat " packageSummary := "Create atom (⚛) representation" packageDescription := """Create atom (⚛) representation for your application, packages and libraries.""" debianPackageDependencies := Seq("java17-runtime-headless") -rpmVendor := "AppThreat" +rpmVendor := "AppThreat" lazy val createDistribution = taskKey[File]("Create a complete atom distribution") createDistribution := { - val distributionFile = file("target/atom.zip") - val zip = (atom / Universal / packageBin).value - IO.copyFile(zip, distributionFile) - println(s"created distribution - resulting files: $distributionFile") - distributionFile + val distributionFile = file("target/atom.zip") + val zip = (atom / Universal / packageBin).value + IO.copyFile(zip, distributionFile) + println(s"created distribution - resulting files: $distributionFile") + distributionFile } ThisBuild / resolvers ++= Seq( Resolver.mavenLocal, Resolver.githubPackages("appthreat/chen"), - "Sonatype OSS" at "https://oss.sonatype.org/content/repositories/public", - "Atlassian" at "https://packages.atlassian.com/mvn/maven-atlassian-external", - "Gradle Releases" at "https://repo.gradle.org/gradle/libs-releases/" + "Sonatype OSS".at("https://oss.sonatype.org/content/repositories/public"), + "Atlassian".at("https://packages.atlassian.com/mvn/maven-atlassian-external"), + "Gradle Releases".at("https://repo.gradle.org/gradle/libs-releases/") ) ThisBuild / assemblyMergeStrategy := { - case "application.conf" => MergeStrategy.concat - case x => MergeStrategy.preferProject + case "application.conf" => MergeStrategy.concat + case x => MergeStrategy.preferProject } ThisBuild / versionScheme := Some("semver-spec") @@ -168,16 +182,24 @@ Global / onChangedBuildSource := ReloadOnSourceChanges Compile / doc / sources := Seq.empty Compile / packageDoc / publishArtifact := false -wartremoverWarnings ++= Seq(Wart.NoNeedImport, Wart.ArrayEquals, Wart.Any, Wart.FinalCaseClass, Wart.FinalVal, Wart.ToString, Wart.TryPartial) +wartremoverWarnings ++= Seq( + Wart.NoNeedImport, + Wart.ArrayEquals, + Wart.Any, + Wart.FinalCaseClass, + Wart.FinalVal, + Wart.ToString, + Wart.TryPartial +) -githubOwner := "appthreat" -githubRepository := "atom" +githubOwner := "appthreat" +githubRepository := "atom" githubSuppressPublicationWarning := true credentials += - Credentials( - "GitHub Package Registry", - "maven.pkg.github.com", - "appthreat", - sys.env.getOrElse("GITHUB_TOKEN", "N/A") - ) + Credentials( + "GitHub Package Registry", + "maven.pkg.github.com", + "appthreat", + sys.env.getOrElse("GITHUB_TOKEN", "N/A") + ) graalVMNativeImageOptions := Seq("-H:+UnlockExperimentalVMOptions", "--no-fallback") diff --git a/codemeta.json b/codemeta.json index 4572874..e26910e 100644 --- a/codemeta.json +++ b/codemeta.json @@ -7,7 +7,7 @@ "downloadUrl": "https://github.com/AppThreat/atom", "issueTracker": "https://github.com/AppThreat/atom/issues", "name": "atom", - "version": "2.0.18", + "version": "2.0.19", "description": "Atom is a novel intermediate representation for next-generation code analysis.", "applicationCategory": "code-analysis", "keywords": [ diff --git a/project/build.properties b/project/build.properties index ee4c672..0b699c3 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version=1.10.2 diff --git a/project/plugins.sbt b/project/plugins.sbt index 07fea36..97c1c8a 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,4 +4,4 @@ addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.4") addSbtPlugin("com.codecommit" % "sbt-github-packages" % "0.5.3") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0") -addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.2.0") +addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.2.1") diff --git a/wrapper/nodejs/astgen.js b/wrapper/nodejs/astgen.js index 302a957..955c042 100755 --- a/wrapper/nodejs/astgen.js +++ b/wrapper/nodejs/astgen.js @@ -62,14 +62,13 @@ const babelSafeParserOptions = { */ const getAllSrcJSAndTSFiles = (src) => Promise.all([ - getAllFiles(src, ".js"), - getAllFiles(src, ".jsx"), - getAllFiles(src, ".cjs"), - getAllFiles(src, ".mjs"), - getAllFiles(src, ".ts"), - getAllFiles(src, ".tsx"), - getAllFiles(src, ".vue"), - getAllFiles(src, ".svelte") + getAllFiles( + src, + undefined, + undefined, + undefined, + new RegExp("\\.(js|jsx|cjs|mjs|ts|tsx|vue|svelte)$") + ) ]); /** @@ -100,6 +99,15 @@ const vueBindRegex = /(:\[)([\s\S]*?)(\])/gi; const vuePropRegex = /\s([.:@])([a-zA-Z]*?=)/gi; const vueOpenImgTag = /()[\s\S]+?)( [^/]>)/gi; +const TSC_FLAGS = + tsc.TypeFormatFlags.NoTruncation | + tsc.TypeFormatFlags.InTypeAlias | + tsc.TypeFormatFlags.WriteArrayAsGenericType | + tsc.TypeFormatFlags.GenerateNamesForShadowedTypeParams | + tsc.TypeFormatFlags.WriteTypeArgumentsOfSignature | + tsc.TypeFormatFlags.UseFullyQualifiedType | + tsc.TypeFormatFlags.NoTypeReduction; + /** * Convert a single vue file to AST */ @@ -132,6 +140,10 @@ function createTsc(srcFiles) { const program = tsc.createProgram(srcFiles, { target: tsc.ScriptTarget.ES2020, module: tsc.ModuleKind.CommonJS, + allowImportingTsExtensions: false, + allowArbitraryExtensions: false, + allowSyntheticDefaultImports: true, + allowUmdGlobalAccess: true, allowJs: true, allowUnreachableCode: true, allowUnusedLabels: true, @@ -150,10 +162,7 @@ function createTsc(srcFiles) { const safeTypeToString = (node) => { try { - return typeChecker.typeToString( - node, - tsc.TypeFormatFlags.NoTruncation | tsc.TypeFormatFlags.InTypeAlias - ); + return typeChecker.typeToString(node, TSC_FLAGS); } catch (err) { return "any"; } @@ -161,11 +170,7 @@ function createTsc(srcFiles) { const safeTypeWithContextToString = (node, context) => { try { - return typeChecker.typeToString( - node, - context, - tsc.TypeFormatFlags.NoTruncation | tsc.TypeFormatFlags.InTypeAlias - ); + return typeChecker.typeToString(node, context, TSC_FLAGS); } catch (err) { return "any"; } @@ -176,10 +181,18 @@ function createTsc(srcFiles) { if ( tsc.isSetAccessor(node) || tsc.isGetAccessor(node) || + tsc.isGetAccessorDeclaration(node) || + tsc.isCallSignatureDeclaration(node) || + tsc.isIndexSignatureDeclaration(node) || + tsc.isClassStaticBlockDeclaration(node) || tsc.isConstructSignatureDeclaration(node) || tsc.isMethodDeclaration(node) || tsc.isFunctionDeclaration(node) || - tsc.isConstructorDeclaration(node) + tsc.isConstructorDeclaration(node) || + tsc.isTypeAliasDeclaration(node) || + tsc.isEnumDeclaration(node) || + tsc.isNamespaceExportDeclaration(node) || + tsc.isImportEqualsDeclaration(node) ) { const signature = typeChecker.getSignatureFromDeclaration(node); const returnType = typeChecker.getReturnTypeOfSignature(signature); @@ -204,8 +217,9 @@ function createTsc(srcFiles) { node ); } - if (!["any", "unknown", "any[]", "unknown[]"].includes(typeStr)) + if (!["any", "unknown", "any[]", "unknown[]"].includes(typeStr)) { seenTypes.set(node.getStart(), typeStr); + } tsc.forEachChild(node, addType); }; @@ -232,7 +246,6 @@ const createJSAst = async (options) => { if (options.tsTypes) { ts = createTsc(srcFiles); } - for (const file of srcFiles) { try { const ast = fileToJsAst(file); diff --git a/wrapper/nodejs/package-lock.json b/wrapper/nodejs/package-lock.json index 37c8cc6..2f54793 100644 --- a/wrapper/nodejs/package-lock.json +++ b/wrapper/nodejs/package-lock.json @@ -1,16 +1,16 @@ { "name": "@appthreat/atom", - "version": "2.0.18", + "version": "2.0.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@appthreat/atom", - "version": "2.0.18", + "version": "2.0.19", "license": "Apache-2.0", "dependencies": { "@babel/parser": "^7.25.6", - "typescript": "^5.5.4", + "typescript": "^5.6.2", "yargs": "^17.7.2" }, "bin": { @@ -1212,9 +1212,10 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/wrapper/nodejs/package.json b/wrapper/nodejs/package.json index e05fc4f..98ba3fa 100644 --- a/wrapper/nodejs/package.json +++ b/wrapper/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@appthreat/atom", - "version": "2.0.18", + "version": "2.0.19", "description": "Create atom (⚛) representation for your application, packages and libraries", "exports": "./index.js", "type": "module", @@ -10,16 +10,16 @@ }, "dependencies": { "@babel/parser": "^7.25.6", - "typescript": "^5.5.4", + "typescript": "^5.6.2", "yargs": "^17.7.2" }, "devDependencies": { "eslint": "8.57.0" }, "bin": { - "atom": "./index.js", - "astgen": "./astgen.js", - "phpastgen": "./phpastgen.js" + "atom": "index.js", + "astgen": "astgen.js", + "phpastgen": "phpastgen.js" }, "engines": { "node": ">=16.0.0"