Open-source project
ShiftLeftSecurity/codepropertygraph avatar
ShiftLeftSecurity/codepropertygraph

codepropertygraph: an open standard for exchanging code as a graph

Code Property Graph: specification, query language, and utilities

638 stars85 forksScalaApache-2.0

At a glance

What is it?
The Code Property Graph repository publishes a language-agnostic, extensible representation of program code for incremental and distributed analysis, together with a query language, generated Scala domain classes and Protocol Buffer bindings for other languages. The specification lives at cpg.joern.io, and the tooling is the input to joern.
Who is it for?
Adopt codepropertygraph if you are building a code analysis tool and want a shared, extensible representation of code rather than a private AST, and you can live with the Scala and sbt build. Skip it if you only need to analyse one language with an existing tool, since joern combines this repository with a language frontend and is the recommended starting point.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A representation of code, not a tool that analyses it

A Code Property Graph is described as an extensible and language-agnostic representation of program code designed for incremental and distributed code analysis, and this repository hosts the base specification together with a build process that generates data structure definitions for accessing the graph with different programming languages. The distinction between the representation and the tools that use it is the point of the project. The stated goal is to publish the Code Property Graph specification as a suggestion for an open standard for the exchange of code in intermediate representations along with analysis results. A standard only makes sense if several tools can read and write the same thing, and the design that follows from that goal is a minimal base schema that can be augmented via extension schemas to enable storage of application-specific data. So a company that wants to attach its own findings to a code graph can define an extension schema rather than inventing a parallel format, and a consumer that does not understand the extension can still read the base graph. The clickable specification is published at cpg.joern.io, and the repository is written in Scala.

Joern is the recommended entry point, not this repository

The README puts a note right at the top, and for first-time users it recommends building joern at the joernio/joern repository instead. The reason given is that joern combines this repository with a C/C++ language frontend to construct a complete code analysis platform. In other words, the code property graph is the data model and the query layer, and something has to turn source code into it. This repository does not do that for you. The one place where the conversion tooling does appear is the test data, since the repository contains sample cpgs in the resources/testcode/cpgs directory and a script to regenerate them, and the README is explicit that running that script requires the proprietary java2cpg installed locally. So there is a clear division of labour. The schema, the generated domain classes, the query primitives and the serialisation format are open and reusable. The frontends that produce a graph from source may not be. That split is worth understanding before you pick this up, because it determines what you can build on and what you have to license or write yourself.

The query DSL makes invalid traversals a compile error

The query language is a typed traversal DSL over the graph, and the README explains what that buys. Queries are provided by the query-primitives subproject, and the DSL ensures that only valid steps are available, so anything else will result in a compile error. The base traversals cover the node types you would expect, and running all of them without errors is described as a good test to ensure that your cpg is valid.

scala
cpg.literal.toList
cpg.file.toList
cpg.namespace.toList
cpg.types.toList
cpg.methodReturn.toList
cpg.parameter.toList
cpg.member.toList
cpg.call.toList
cpg.local.toList
cpg.identifier.toList
cpg.argument.toList
cpg.typeDecl.toList
cpg.method.toList

The chaining then reads like a path through the program. A method name lookup followed by its parameters and its defining type is a two-line query.

scala
cpg.method.name("getAccountList").parameter.toList
scala
cpg.method.name("getAccountList").definingTypeDecl.toList.head

The compile-time guarantee is the substantive difference from writing raw Gremlin or building your own graph queries. A traversal step that does not exist on the node type you are on is rejected by the compiler rather than producing an empty result at runtime, which is the difference between a query bug that shows up immediately and one that shows up as a missing finding. The cost is that queries live in Scala, and the project leans on that. A Scala developer gets the full type checking, and a Python or C++ consumer of the same graph does not, which is why the Protocol Buffer bindings and the cross-language story matter.

Domain classes are generated from the schema and checked in

The Scala domain classes under domainClasses/src/main/generated/ are generated from the schema defined in schema/src/main/scala/io/shiftleft/codepropertygraph/schema/, and they are checked into the repository, so you only need to regenerate them when the schema changes. Generation is handled by the sbt-flatgraph plugin via the generateDomainClasses task in the schema subproject, which reads CpgSchema.instance, that is the composition of all schema modules, and writes Scala source files to the generated directory. The task is wired as a dependency of domainClasses/compile and stage, so it runs automatically when you compile or stage, and you can also run it on its own.

bash
sbt compile   # triggers generateDomainClasses, then compiles
sbt stage     # triggers generateDomainClasses, then packages
bash
sbt schema/generateDomainClasses

This is a design decision with a practical upside. Generated code in version control is unusual, and it normally causes merge conflicts and review noise, but here the generated classes are the public API of the library and their diffs are a readable record of what changed in the schema. When a reviewer sees a change in the generated files, that is a schema change, and the schema is the thing the specification is about. The cost is that the checked-in files have to be regenerated and committed whenever the schema moves, and a build that regenerates them anyway will produce a diff the developer has to notice.

Building it, and the Git LFS and Bazel pieces

The build process has been verified on Linux, and the README says it should be possible to build on OS X and BSD systems as well. The prerequisites are a Java runtime 11, the Scala build tool SBT, Git-lfs and Protocol Buffer. The LFS part matters for anyone cloning the repository, since some binary files required for testing are managed through it: if you have not cloned the repository yet you run git lfs install, and if you have cloned it already you additionally run git lfs pull from within the repository. Additional build-time dependencies are downloaded automatically as part of the build, and to build and install into your local Maven cache you issue sbt clean test publishM2. The repository tree is more complex than a single sbt project. There is a .bazelrc, a .bazelversion, MODULE.bazel with a lock file, bazel/ and BUILD files, alongside build.sbt and a project/ directory, so the project is being migrated towards or mixed with Bazel while keeping sbt. There are also maven_install.json and format_maven_install.json, a format_maven_install.json used by rules_jvm_external style pinning, an updateDependencies.sh script, and a private-key.pem.enc file at the top level, which is part of the release signing setup. The samples directory contains a customcpg example and a pass example.

Protocol Buffer bindings, and the Tinkergraph in-memory reference

The jar artifact contains a Protocol Buffer definition file, cpg.proto, that you can use to generate your own language-specific bindings, and the README gives the command sequence for C++ and Python.

bash
sbt package
cd codepropertygraph/target
mkdir cpp python
protoc --cpp_out=cpp --python_out=python cpg.proto

This is the piece that makes the open standard claim credible. A Python consumer does not have to run the Scala side at all, it can generate its own bindings from the proto file and read a graph produced by a Scala tool. Loading a graph into a reference database is the other half of the story, and the README shows how to load a cpg into ShiftLeft Tinkergraph in the sbt console, with the loader call and the two imports it needs.

scala
import io.shiftleft.codepropertygraph.generated.Cpg
import io.shiftleft.semanticcpg.language._
val cpg = io.shiftleft.codepropertygraph.cpgloading.CpgLoader.load("./resources/testcode/cpgs/hello-shiftleft-0.0.5/cpg.bin.zip")

The sbt console entry point is sbt semanticcpg/console, and the reference database is the in-memory Tinkergraph, with the underlying storage engine being a separate project, ShiftLeft OverflowDB, linked from the further reading section. The distinction between the format and the database is worth holding on to. The code property graph is a schema and a set of node and edge types, and the database is one implementation of a store for it, and the fact that they are separate projects is what allows a different store to be substituted without invalidating the data.

Releasing the schema, and the project state

The schema itself can be dumped as JSON with a bash script in the repository.

bash
./schema2json.sh
Schema written to: /tmp/schema.json

That output is the practical form of the specification for anyone who wants to read it without a Scala toolchain, and it is also the artifact a proposed extension would be written against. The release history shows a high cadence, with v1.7.81 on 2026-10-20, v1.7.80 on 2026-10-19 and v1.7.79 also on 2026-10-19, and the last push to the repository was on 2026-10-20, the same day as the newest tag, with the repository not archived. Frequent patch releases on a schema project are a reasonable pattern, since the schema evolves as frontends add node types and the version number tracks compatibility for downstream tools. The licence is Apache-2.0, which is the right choice for a specification intended to become a shared standard, since it lets anyone implement it without a licensing negotiation. The further reading section points at the graph traversal pattern paper and at the original Modeling and Discovering Vulnerabilities with Code Property Graphs paper by Yamaguchi and colleagues, which is the reference that explains why the model looks the way it does. Code style is checked by scalafmt, and if a pull request build fails the formatting check you run sbt scalafmt and submit the change with the rest.

Editorial conclusion

Adopt codepropertygraph if you are building a code analysis tool and want a shared, extensible representation of code rather than a private AST, and you can live with the Scala and sbt build. Skip it if you only need to analyse one language with an existing tool, since joern combines this repository with a language frontend and is the recommended starting point. Verify first that the version you depend on matches the query DSL you write, that the schema extension mechanism fits your own data, and that the proprietary java2cpg dependency is not required for the workflow you intend to run.

Frequently asked questions

What is the Code Property Graph and what does this repository contain?

A Code Property Graph is an extensible, language-agnostic representation of program code designed for incremental and distributed code analysis. This repository hosts the base specification together with a build process that generates data structure definitions for accessing the graph from different programming languages, and the clickable specification is at cpg.joern.io.

How do I use codepropertygraph as a dependency?

Add it to your build.sbt with libraryDependencies += "io.shiftleft" %% "codepropertygraph" % "x.y.z". The artifact is published to Maven Central and the version follows the release tags.

How do I query a code property graph?

Queries come from the query-primitives subproject and use a typed traversal DSL where only valid steps are available, so an invalid traversal is a compile error. In the sbt console, sbt semanticcpg/console opens a session where you can load a cpg with CpgLoader.load and then traverse it, for example cpg.method.name("getAccountList").parameter.toList.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. ShiftLeftSecurity/codepropertygraph on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/shiftleftsecurity-codepropertygraph.svg)](https://hysenlabs.com/projects/shiftleftsecurity-codepropertygraph)

Was this useful?