All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.php2cpg.passes.DependencyPass.scala Maven / Gradle / Ivy

The newest version!
package io.joern.php2cpg.passes

import better.files.File
import io.joern.php2cpg.parser.Domain.PhpOperators
import io.shiftleft.codepropertygraph.generated.nodes.{NewDependency, NewTag}
import io.shiftleft.codepropertygraph.generated.{Cpg, EdgeTypes}
import io.shiftleft.passes.ForkJoinParallelCpgPass
import org.slf4j.LoggerFactory
import upickle.default.*

import scala.annotation.targetName
import scala.util.{Failure, Success, Try}

/** Parses the `composer.json` file for all `require` dependencies.
  */
class DependencyPass(cpg: Cpg, composerPaths: List[String]) extends ForkJoinParallelCpgPass[File](cpg) {

  private val logger = LoggerFactory.getLogger(getClass)

  override def generateParts(): Array[File] = composerPaths.map(File(_)).toArray

  override def runOnPart(builder: DiffGraphBuilder, composerFile: File): Unit = {
    val composer =
      composerFile.inputStream.apply(is => Try(read[Composer](ujson.Readable.fromByteArray(is.readAllBytes())))) match {
        case Failure(exception) =>
          logger.error(s"Unable to deserialize `${composerFile.pathAsString}`", exception)
          Composer()
        case Success(composer) => composer
      }
    composer.require.foreach { case (name, version) =>
      builder.addNode(NewDependency().name(name).version(version))
    }
    // For `autoload`, the version is the matcher path prefixed with `autoload`
    composer.autoload.`psr-4`.foreach {
      case (namespace, PsrString(name)) =>
        builder.addNode(NewDependency().name(namespace).version(s"${PhpOperators.autoload}$name"))
      case (namespace, PsrArray(array)) =>
        array.foreach { name =>
          builder.addNode(NewDependency().name(namespace).version(s"${PhpOperators.autoload}$name"))
        }
    }
  }

}

sealed trait PsrStringOrArray {
  def obj: String | Array[String]
}

case class PsrString(obj: String) extends PsrStringOrArray

case class PsrArray(obj: Array[String]) extends PsrStringOrArray

object PsrStringOrArray:
  given ReadWriter[PsrStringOrArray] = {
    val logger = LoggerFactory.getLogger(getClass)

    readwriter[ujson.Value]
      .bimap[PsrStringOrArray](
        x =>
          x.obj match {
            case o: String        => ujson.Str(o)
            case o: Array[String] => ujson.Arr(o.map(ujson.Str.apply)*)
          },
        {
          case json @ (j: ujson.Str) => PsrString(json.str)
          case json @ (j: ujson.Arr) => PsrArray(json.arr.map(_.str).toArray)
          case x =>
            logger.warn(s"Unexpected value type for `autoload.psr-4`: ${x.getClass}")
            PsrString("")
        }
      )
  }

case class Autoload(
  @targetName("psr0") `psr-0`: Map[String, PsrStringOrArray] = Map.empty,
  @targetName("psr4") `psr-4`: Map[String, PsrStringOrArray] = Map.empty
) derives ReadWriter

case class Composer(require: Map[String, String] = Map.empty, autoload: Autoload = Autoload()) derives ReadWriter




© 2015 - 2024 Weber Informatics LLC | Privacy Policy