diff --git a/README.md b/README.md index d09ab24..fd20c0d 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Command: usages [options] Extract local variable and parameter usages --min-num-calls the minimum number of calls required for a usage slice - defaults to 1. --include-source includes method source code in the slices - defaults to false. + --extract-endpoints extract http endpoints and convert to openapi format using atom-tools - defaults to false. Command: reachables [options] Extract reachable data-flow slices based on automated framework tags --source-tag source tag - defaults to framework-input. @@ -133,6 +134,17 @@ atom usages -o app.atom --slice-outfile usages.json -l java . Learn more about [slices](./specification/docs/slices.md) or view some [samples](https://github.com/AppThreat/atom-samples) +### Extract HTTP endpoints in openapi format using atom-tools + +Atom can automatically invoke [atom-tools](https://github.com/AppThreat/atom-tools) `convert` command to extract http endpoints from the usages slices. Pass the argument `--extract-endpoints` to enable this feature. + +```shell +pip install atom-tools +atom usages --extract-endpoints -o app.atom --slice-outfile usages.json -l java . +``` + +A file called `openapi.generated.json` would be created with the endpoints information. + ### Export atom to graphml or dot format It is possible to export each method along with data dependencies in an atom to graphml or dot format. Simply pass `--export` to enable this feature. @@ -158,15 +170,15 @@ atom -o app.atom -l java --export-atom --export-dir --with-data-dep ## container usage ```shell -docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom --help -# podman run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom --help +docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom atom --help +# podman run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom atom --help ``` Example for java project. ```shell -docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom -l java -o /app/app.atom /app -# podman run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom -l java -o /app/app.atom /app +docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom atom -l java -o /app/app.atom /app +# podman run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom atom -l java -o /app/app.atom /app ``` ## Languages supported diff --git a/build.sbt b/build.sbt index 47f1450..5b54143 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ name := "atom" ThisBuild / organization := "io.appthreat" -ThisBuild / version := "2.0.8" +ThisBuild / version := "2.0.9" ThisBuild / scalaVersion := "3.3.1" val chenVersion = "2.0.8" diff --git a/ci/Dockerfile b/ci/Dockerfile index 645617e..fd0e702 100644 --- a/ci/Dockerfile +++ b/ci/Dockerfile @@ -9,7 +9,7 @@ LABEL maintainer="appthreat" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.title="atom" \ org.opencontainers.image.description="Container image for AppThreat atom" \ - org.opencontainers.docker.cmd="docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom -o /app/app.atom -l java /app" + org.opencontainers.docker.cmd="docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom atom -o /app/app.atom -l java /app" ARG MAVEN_VERSION=3.9.6 @@ -18,6 +18,8 @@ ENV MAVEN_VERSION=$MAVEN_VERSION \ ANDROID_HOME=/opt/android-sdk-linux \ JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" \ PHP_PARSER_BIN=/opt/vendor/bin/php-parse \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING="utf-8" \ COMPOSER_ALLOW_SUPERUSER=1 ENV PATH=/opt/bin:/opt/vendor/bin:${PATH}:${MAVEN_HOME}/bin:/usr/local/bin/:/root/.local/bin:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools: @@ -38,8 +40,13 @@ RUN set -e; \ esac; \ echo -e "[nodejs]\nname=nodejs\nstream=20\nprofiles=\nstate=enabled\n" > /etc/dnf/modules.d/nodejs.module \ && microdnf install -y gcc git-core php php-cli php-curl php-zip php-bcmath php-json php-pear php-mbstring php-devel make \ + python3.11 python3.11-devel python3.11-pip \ wget bash glibc-common glibc-all-langpacks java-21-openjdk-headless \ pcre2 findutils which tar gzip zip unzip sudo nodejs \ + && alternatives --install /usr/bin/python3 python /usr/bin/python3.11 1 \ + && python3 --version \ + && node --version \ + && python3 -m pip install --upgrade pip poetry atom-tools \ && curl -s "https://get.sdkman.io" | bash \ && source "$HOME/.sdkman/bin/sdkman-init.sh" \ && echo -e "sdkman_auto_answer=true\nsdkman_selfupdate_feature=false\nsdkman_auto_env=true\nsdkman_curl_connect_timeout=20\nsdkman_curl_max_time=0" >> $HOME/.sdkman/etc/config \ @@ -77,4 +84,4 @@ RUN unzip -q atom.zip \ && which phpastgen \ && microdnf clean all -ENTRYPOINT ["/opt/bin/atom"] +CMD ["/opt/bin/atom"] diff --git a/ci/Dockerfile.sle b/ci/Dockerfile.sle index b23b5b7..5403242 100644 --- a/ci/Dockerfile.sle +++ b/ci/Dockerfile.sle @@ -9,12 +9,14 @@ LABEL maintainer="appthreat" \ org.opencontainers.image.licenses="Apache-2.0" \ org.opencontainers.image.title="atom" \ org.opencontainers.image.description="Container image for AppThreat atom" \ - org.opencontainers.docker.cmd="docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom-sle -o /app/app.atom -l java /app" + org.opencontainers.docker.cmd="docker run --rm -v /tmp:/tmp -v $HOME:$HOME -v $(pwd):/app:rw -it ghcr.io/appthreat/atom-sle atom -o /app/app.atom -l java /app" ENV ANDROID_HOME=/opt/android-sdk-linux \ JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8" \ PHP_PARSER_BIN=/opt/vendor/bin/php-parse \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING="utf-8" \ COMPOSER_ALLOW_SUPERUSER=1 ENV PATH=/opt/bin:/opt/vendor/bin:${PATH}:/usr/local/bin/:/root/.local/bin:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools: @@ -34,6 +36,9 @@ RUN set -e; \ *) echo >&2 "error: unsupported architecture: '$ARCH_NAME'"; exit 1 ;; \ esac; \ zypper --non-interactive install -l --no-recommends php8 php8-cli php8-curl php8-zip php8-bcmath php8-pear php8-mbstring php8-devel \ + && python3 --version \ + && node --version \ + && python3 -m pip install --upgrade pip poetry atom-tools \ && mkdir -p ${ANDROID_HOME}/cmdline-tools \ && curl -L https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip -o ${ANDROID_HOME}/cmdline-tools/android_tools.zip \ && unzip ${ANDROID_HOME}/cmdline-tools/android_tools.zip -d ${ANDROID_HOME}/cmdline-tools/ \ @@ -64,4 +69,4 @@ RUN unzip -q atom.zip \ && which phpastgen \ && zypper clean -a -ENTRYPOINT ["/opt/bin/atom"] +CMD ["/opt/bin/atom"] diff --git a/codemeta.json b/codemeta.json index 6d36dfe..2e63f68 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.8", + "version": "2.0.9", "description": "Atom is a novel intermediate representation for next-generation code analysis.", "applicationCategory": "code-analysis", "keywords": [ diff --git a/src/main/scala/io/appthreat/atom/Atom.scala b/src/main/scala/io/appthreat/atom/Atom.scala index d1118cb..e08adcc 100644 --- a/src/main/scala/io/appthreat/atom/Atom.scala +++ b/src/main/scala/io/appthreat/atom/Atom.scala @@ -31,6 +31,7 @@ import io.appthreat.pysrc2cpg.{ import io.appthreat.x2cpg.passes.base.AstLinkerPass import io.appthreat.x2cpg.passes.frontend.XTypeRecoveryConfig import io.appthreat.x2cpg.passes.taggers.{CdxPass, ChennaiTagsPass, EasyTagsPass} +import io.appthreat.x2cpg.utils.ExternalCommand import io.shiftleft.codepropertygraph.cpgloading.CpgLoaderConfig import io.shiftleft.codepropertygraph.generated.{Cpg, Languages} import io.shiftleft.semanticcpg.language.* @@ -183,7 +184,7 @@ object Atom: ) cmd("parsedeps") .text("Extract dependencies from the build file and imports") - .action((_, c) => AtomParseDepsConfig().withRemoveAtom(true)) + .action((_, *) => AtomParseDepsConfig().withRemoveAtom(true)) cmd("data-flow") .text("Extract backward data-flow slices") .action((_, _) => AtomDataFlowConfig().withDataDependencies(true)) @@ -207,7 +208,7 @@ object Atom: ) cmd("usages") .text("Extract local variable and parameter usages") - .action((_, c) => AtomUsagesConfig().withRemoveAtom(true)) + .action((_, *) => AtomUsagesConfig().withRemoveAtom(true)) .children( opt[Int]("min-num-calls") .text(s"the minimum number of calls required for a usage slice - defaults to 1.") @@ -222,11 +223,20 @@ object Atom: c match case c: AtomUsagesConfig => c.copy(includeMethodSource = true) case _ => c + ), + opt[Unit]("extract-endpoints") + .text( + s"extract http endpoints and convert to openapi format using atom-tools - defaults to false." + ) + .action((_, c) => + c match + case c: AtomUsagesConfig => c.copy(extractEndpoints = true) + case _ => c ) ) cmd("reachables") .text("Extract reachable data-flow slices based on automated framework tags") - .action((_, c) => AtomReachablesConfig().withDataDependencies(true)) + .action((_, *) => AtomReachablesConfig().withDataDependencies(true)) .children( opt[String]("source-tag") .text(s"source tag - defaults to framework-input.") @@ -263,7 +273,7 @@ object Atom: def main(args: Array[String]): Unit = run(args) match - case Right(msg) => + case Right(_) => case Left(errMsg) => if errMsg == null then println("Unexpected error") @@ -351,8 +361,20 @@ object Atom: AtomDataFlowSlice(x, DataFlowGraph.buildFromSlice(x).paths).toJson ) saveSlice(config.outputSliceFile, atomDataFlowSliceJson) - case _: UsagesConfig => + case u: UsagesConfig => saveSlice(config.outputSliceFile, sliceCpg(ag).map(_.toJson)) + if u.extractEndpoints then + val result = ExternalCommand.run( + s"atom-tools convert -i ${config.outputSliceFile} -t ${config.language} -f openapi3.1.0 -q -o ${config.inputPath.pathAsString}${java.io.File.separator}openapi.generated.json", + "." + ) + result match + case Success(_) => + println("openapi.generated.json created successfully.") + case Failure(exception) => + println( + s"Failed to run atom-tools. Use the atom container image or perform 'pip install atom-tools' and re-run this command. Exception: ${exception.getMessage}" + ) case _: ReachablesConfig => saveSlice(config.outputSliceFile, sliceCpg(ag).map(_.toJson)) case x: AtomParseDepsConfig => @@ -392,7 +414,8 @@ object Atom: UsagesConfig( config.minNumCalls, config.excludeOperatorCalls, - !config.includeMethodSource + !config.includeMethodSource, + config.extractEndpoints ) case config: AtomReachablesConfig => ReachablesConfig( @@ -425,130 +448,128 @@ object Atom: val outputAtomFile = config.outputAtomFile.pathAsString // Create a new atom def createAtom = - ( - language match - case "H" | "HPP" => - new C2Atom() - .createCpg( - CConfig( - includeComments = false, - logProblems = false, - includePathsAutoDiscovery = false - ) - .withLogPreprocessor(false) - .withInputPath(config.inputPath.pathAsString) - .withOutputPath(outputAtomFile) - .withFunctionBodies(false) - .withIgnoredFilesRegex( - COMMON_IGNORE_REGEX - ) - ) - case Languages.C | Languages.NEWC | "CPP" | "C++" => - new C2Cpg() - .createCpgWithOverlays( - CConfig( - includeComments = false, - logProblems = false, - includePathsAutoDiscovery = true - ) - .withLogPreprocessor(false) - .withInputPath(config.inputPath.pathAsString) - .withOutputPath(outputAtomFile) - .withFunctionBodies(true) - .withIgnoredFilesRegex( - COMMON_IGNORE_REGEX - ) - .withIncludePaths(C2ATOM_INCLUDE_PATH) - ) - case "JAR" | "JIMPLE" | "ANDROID" | "APK" | "DEX" => - new Jimple2Cpg() - .createCpgWithOverlays( - JimpleConfig(android = ANDROID_JAR_PATH) - .withInputPath(config.inputPath.pathAsString) - .withOutputPath(outputAtomFile) - .withFullResolver(true) + language match + case "H" | "HPP" => + new C2Atom() + .createCpg( + CConfig( + includeComments = false, + logProblems = false, + includePathsAutoDiscovery = false ) - case Languages.JAVA | Languages.JAVASRC => - new JavaSrc2Cpg() - .createCpgWithOverlays( - JavaConfig( - fetchDependencies = true, - inferenceJarPaths = JAR_INFERENCE_PATHS, - enableTypeRecovery = true, - delombokMode = Some(DEFAULT_DELOMBOK_MODE) - ) - .withInputPath(config.inputPath.pathAsString) - .withDefaultIgnoredFilesRegex( - List( - "\\..*".r, - ".*build/(generated|intermediates|outputs|tmp).*" r, - ".*src/test.*" r - ) - ) - .withOutputPath(outputAtomFile) + .withLogPreprocessor(false) + .withInputPath(config.inputPath.pathAsString) + .withOutputPath(outputAtomFile) + .withFunctionBodies(false) + .withIgnoredFilesRegex( + COMMON_IGNORE_REGEX + ) + ) + case Languages.C | Languages.NEWC | "CPP" | "C++" => + new C2Cpg() + .createCpgWithOverlays( + CConfig( + includeComments = false, + logProblems = false, + includePathsAutoDiscovery = true ) - case Languages.JSSRC | Languages.JAVASCRIPT | "JS" | "TS" | "TYPESCRIPT" => - new JsSrc2Cpg() - .createCpgWithOverlays( - JSConfig() - .withDisableDummyTypes(true) - .withTypePropagationIterations(TYPE_PROPAGATION_ITERATIONS) - .withInputPath(config.inputPath.pathAsString) - .withOutputPath(outputAtomFile) + .withLogPreprocessor(false) + .withInputPath(config.inputPath.pathAsString) + .withOutputPath(outputAtomFile) + .withFunctionBodies(true) + .withIgnoredFilesRegex( + COMMON_IGNORE_REGEX + ) + .withIncludePaths(C2ATOM_INCLUDE_PATH) + ) + case "JAR" | "JIMPLE" | "ANDROID" | "APK" | "DEX" => + new Jimple2Cpg() + .createCpgWithOverlays( + JimpleConfig(android = ANDROID_JAR_PATH) + .withInputPath(config.inputPath.pathAsString) + .withOutputPath(outputAtomFile) + .withFullResolver(true) + ) + case Languages.JAVA | Languages.JAVASRC => + new JavaSrc2Cpg() + .createCpgWithOverlays( + JavaConfig( + fetchDependencies = true, + inferenceJarPaths = JAR_INFERENCE_PATHS, + enableTypeRecovery = true, + delombokMode = Some(DEFAULT_DELOMBOK_MODE) ) - .map { ag => - new JavaScriptInheritanceNamePass(ag).createAndApply() - new ConstClosurePass(ag).createAndApply() - new ImportResolverPass(ag).createAndApply() - new JavaScriptTypeRecoveryPass(ag).createAndApply() - new TypeHintPass(ag).createAndApply() - ag - } - case Languages.PYTHONSRC | Languages.PYTHON | "PY" => - new Py2CpgOnFileSystem() - .createCpgWithOverlays( - PyConfig() - .withDisableDummyTypes(true) - .withTypePropagationIterations(TYPE_PROPAGATION_ITERATIONS) - .withInputPath(config.inputPath.pathAsString) - .withOutputPath(outputAtomFile) - .withDefaultIgnoredFilesRegex(List("\\..*".r)) - .withIgnoredFilesRegex( - ".*(samples|examples|test|tests|unittests|docs|virtualenvs|venv|benchmarks|tutorials|noxfile).*" + .withInputPath(config.inputPath.pathAsString) + .withDefaultIgnoredFilesRegex( + List( + "\\..*".r, + ".*build/(generated|intermediates|outputs|tmp).*" r, + ".*src/test.*" r ) - ) - .map { ag => - new PythonImportsPass(ag).createAndApply() - new PyImportResolverPass(ag).createAndApply() - new DynamicTypeHintFullNamePass(ag).createAndApply() - new PythonInheritanceNamePass(ag).createAndApply() - new PythonTypeRecoveryPass( - ag, - XTypeRecoveryConfig(enabledDummyTypes = false) ) - .createAndApply() - new PythonTypeHintCallLinker(ag).createAndApply() - new AstLinkerPass(ag).createAndApply() - ag - } - case Languages.PHP => - new Php2Atom().createCpgWithOverlays( - PhpConfig() - .withDisableDummyTypes(true) - .withInputPath(config.inputPath.pathAsString) - .withOutputPath(outputAtomFile) - .withDefaultIgnoredFilesRegex(List("\\..*".r)) - .withIgnoredFilesRegex(".*(samples|examples|docs|tests).*") - ).map { ag => - new PhpSetKnownTypesPass(ag).createAndApply() - ag - } - case _ => Failure( - new RuntimeException( - s"No language frontend supported for language '$language'" + .withOutputPath(outputAtomFile) + ) + case Languages.JSSRC | Languages.JAVASCRIPT | "JS" | "TS" | "TYPESCRIPT" => + new JsSrc2Cpg() + .createCpgWithOverlays( + JSConfig() + .withDisableDummyTypes(true) + .withTypePropagationIterations(TYPE_PROPAGATION_ITERATIONS) + .withInputPath(config.inputPath.pathAsString) + .withOutputPath(outputAtomFile) + ) + .map { ag => + new JavaScriptInheritanceNamePass(ag).createAndApply() + new ConstClosurePass(ag).createAndApply() + new ImportResolverPass(ag).createAndApply() + new JavaScriptTypeRecoveryPass(ag).createAndApply() + new TypeHintPass(ag).createAndApply() + ag + } + case Languages.PYTHONSRC | Languages.PYTHON | "PY" => + new Py2CpgOnFileSystem() + .createCpgWithOverlays( + PyConfig() + .withDisableDummyTypes(true) + .withTypePropagationIterations(TYPE_PROPAGATION_ITERATIONS) + .withInputPath(config.inputPath.pathAsString) + .withOutputPath(outputAtomFile) + .withDefaultIgnoredFilesRegex(List("\\..*".r)) + .withIgnoredFilesRegex( + ".*(samples|examples|test|tests|unittests|docs|virtualenvs|venv|benchmarks|tutorials|noxfile).*" + ) ) + .map { ag => + new PythonImportsPass(ag).createAndApply() + new PyImportResolverPass(ag).createAndApply() + new DynamicTypeHintFullNamePass(ag).createAndApply() + new PythonInheritanceNamePass(ag).createAndApply() + new PythonTypeRecoveryPass( + ag, + XTypeRecoveryConfig(enabledDummyTypes = false) + ) + .createAndApply() + new PythonTypeHintCallLinker(ag).createAndApply() + new AstLinkerPass(ag).createAndApply() + ag + } + case Languages.PHP => + new Php2Atom().createCpgWithOverlays( + PhpConfig() + .withDisableDummyTypes(true) + .withInputPath(config.inputPath.pathAsString) + .withOutputPath(outputAtomFile) + .withDefaultIgnoredFilesRegex(List("\\..*".r)) + .withIgnoredFilesRegex(".*(samples|examples|docs|tests).*") + ).map { ag => + new PhpSetKnownTypesPass(ag).createAndApply() + ag + } + case _ => Failure( + new RuntimeException( + s"No language frontend supported for language '$language'" ) - ) + ) // Should we reuse or create the atom def getOrCreateAtom = config match diff --git a/src/main/scala/io/appthreat/atom/package.scala b/src/main/scala/io/appthreat/atom/package.scala index 535c3be..39994d7 100644 --- a/src/main/scala/io/appthreat/atom/package.scala +++ b/src/main/scala/io/appthreat/atom/package.scala @@ -66,7 +66,8 @@ package object atom: case class AtomUsagesConfig( minNumCalls: Int = 1, excludeOperatorCalls: Boolean = true, - includeMethodSource: Boolean = false + includeMethodSource: Boolean = false, + extractEndpoints: Boolean = false ) extends AtomConfig case class AtomReachablesConfig( diff --git a/src/main/scala/io/appthreat/atom/slicing/UsageSlicing.scala b/src/main/scala/io/appthreat/atom/slicing/UsageSlicing.scala index 642bf94..c5716ba 100644 --- a/src/main/scala/io/appthreat/atom/slicing/UsageSlicing.scala +++ b/src/main/scala/io/appthreat/atom/slicing/UsageSlicing.scala @@ -76,11 +76,11 @@ object UsageSlicing: .groupBy { case (scope, _) => scope } .view .filterNot((m, _) => - (m.fullName.startsWith("" - ) || m.name.startsWith("")) + ) || m.name.startsWith("") ) .sortBy(_._1.fullName) .map { case (method, slices) => @@ -246,7 +246,17 @@ object UsageSlicing: m.columnNumber.map(_.intValue()) ) ) - .l, + .l ++ call.argument.inCall.map(c => + ObservedCall( + c.code.takeWhile(_ != '('), + Option(c.code), + c.argument.map(_.code.replaceAll("\"", "")).toList, + "", + Option(true), + c.lineNumber.map(_.intValue()), + c.columnNumber.map(_.intValue()) + ) + ).l, call.location.filename, call.lineNumber.map(_.intValue()), call.columnNumber.map(_.intValue()) @@ -548,12 +558,27 @@ object UsageSlicing: if baseCallCode.contains(" ") then baseCallCode = baseCallCode.split(" ").last // Retain the full code for route detection purposes - if language.get == Languages.JSSRC && (baseCallCode.startsWith( - "route" - ) || baseCallCode.startsWith("app")) - then - baseCallCode = baseCall.code.replaceAll("\n", "").replaceAll(" ", "") + if language.get == Languages.JSSRC then + if baseCallCode.startsWith( + "app.use" + ) + then + if baseCall.argument.nonEmpty && baseCall.argument.isLiteral.nonEmpty + then + baseCallCode = + baseCall.argument.isLiteral.filterNot(_.code == "*").head.code + else if baseCallCode.startsWith( + "route" + ) || baseCallCode.startsWith("app") + then + baseCallCode = baseCall.code + .replaceAll("\n", "\\n").replaceAll( + " {4}", + " {2}" + ).replaceAll(" {2}", "\\t") + end if resolvedMethod = Option(baseCallCode) + end if Option( ObservedCall( callName.get, diff --git a/src/main/scala/io/appthreat/atom/slicing/package.scala b/src/main/scala/io/appthreat/atom/slicing/package.scala index 1052c9a..e8aad5b 100644 --- a/src/main/scala/io/appthreat/atom/slicing/package.scala +++ b/src/main/scala/io/appthreat/atom/slicing/package.scala @@ -75,7 +75,8 @@ package object slicing: case class UsagesConfig( minNumCalls: Int = 1, excludeOperatorCalls: Boolean = true, - excludeMethodSource: Boolean = true + excludeMethodSource: Boolean = true, + extractEndpoints: Boolean = false ) extends BaseConfig case class ReachablesConfig( diff --git a/wrapper/nodejs/package-lock.json b/wrapper/nodejs/package-lock.json index 3a384fb..85c6a2a 100644 --- a/wrapper/nodejs/package-lock.json +++ b/wrapper/nodejs/package-lock.json @@ -1,16 +1,16 @@ { "name": "@appthreat/atom", - "version": "2.0.8", + "version": "2.0.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@appthreat/atom", - "version": "2.0.8", + "version": "2.0.9", "license": "Apache-2.0", "dependencies": { - "@babel/parser": "^7.23.9", - "typescript": "^5.3.3", + "@babel/parser": "^7.24.0", + "typescript": "^5.4.2", "yargs": "^17.7.2" }, "bin": { @@ -19,7 +19,7 @@ "phpastgen": "phpastgen.js" }, "devDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0" }, "engines": { "node": ">=16.0.0" @@ -35,9 +35,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -93,22 +93,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -129,9 +129,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -391,16 +391,16 @@ } }, "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -1172,9 +1172,9 @@ } }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/wrapper/nodejs/package.json b/wrapper/nodejs/package.json index 72173d2..dc6347a 100644 --- a/wrapper/nodejs/package.json +++ b/wrapper/nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@appthreat/atom", - "version": "2.0.8", + "version": "2.0.9", "description": "Create atom (⚛) representation for your application, packages and libraries", "exports": "./index.js", "type": "module", @@ -9,12 +9,12 @@ "lint": "eslint *.mjs *.js" }, "dependencies": { - "@babel/parser": "^7.23.9", - "typescript": "^5.3.3", + "@babel/parser": "^7.24.0", + "typescript": "^5.4.2", "yargs": "^17.7.2" }, "devDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0" }, "bin": { "atom": "./index.js",