1 | ![Snyk logo](https://snyk.io/style/asset/logo/snyk-print.svg)
|
2 |
|
3 | ***
|
4 |
|
5 | [![Known Vulnerabilities](https://snyk.io/test/npm/@snyk/dep-graph/badge.svg)](https://snyk.io/test/npm/@snyk/dep-graph)
|
6 |
|
7 | Snyk helps you find, fix and monitor for known vulnerabilities in your dependencies, both on an ad hoc basis and as part of your CI (Build) system.
|
8 |
|
9 | # Snyk dep-graph
|
10 |
|
11 | This library provides a time and space efficient representation of a resolved package dependency graph, which can be used to construct, query and de/serialize dep-graphs.
|
12 |
|
13 | ## The Graph
|
14 |
|
15 | A directed graph, where a node represents a package instance and an edge from node `foo` to node `bar` means `bar` is a dependency of `foo`.
|
16 |
|
17 | A package (`name@version`) can have several different nodes (i.e. instances) in the graph. This flexibility is useful for some ecosystems, for example:
|
18 |
|
19 | * in `npm` due to conflict-resolutions by duplication. e.g. try to `npm i tap@5.7` and then run `npm ls` and look for `strip-ansi@3.0.1`. You'll see that in some instances it depends on `ansi-regex@2.0.0` while in others on `ansi-regex@2.1.1`.
|
20 | * in `maven` due to "exclusion" rules. A dependency `foo` can be declared in the `pom.xml` such that some of it's sub-dependencies are excluded via the `<exclusions>` tag. If the same dependency is required elsewhere without (or with different) exclusions then `foo` can appear in the tree with different sub-trees.
|
21 |
|
22 | This can also be used to break cycles in the graph, e.g.:
|
23 |
|
24 | instead of:
|
25 | ```
|
26 | A -> B -> C -> A
|
27 | ```
|
28 | can have:
|
29 | ```
|
30 | A -> B -> C -> A'
|
31 | ```
|
32 |
|
33 | ## API Reference
|
34 |
|
35 | ### `DepGraph`
|
36 |
|
37 | #### Interface
|
38 |
|
39 | A dep-graph instance can be queried using the following interface:
|
40 |
|
41 | ```typescript
|
42 | export interface DepGraph {
|
43 | readonly pkgManager: {
|
44 | name: string;
|
45 | version?: string;
|
46 | repositories?: Array<{
|
47 | alias: string;
|
48 | }>;
|
49 | };
|
50 | readonly rootPkg: {
|
51 | name: string;
|
52 | version?: string;
|
53 | };
|
54 | // all unique packages in the graph (including root package)
|
55 | getPkgs(): Array<{
|
56 | name: string;
|
57 | version?: string;
|
58 | }>;
|
59 | // all unique packages in the graph, except the root package
|
60 | getDepPkgs(): Array<{
|
61 | name: string;
|
62 | version?: string;
|
63 | }>;
|
64 | pkgPathsToRoot(pkg: Pkg): Array<Array<{
|
65 | name: string;
|
66 | version?: string;
|
67 | }>>;
|
68 | directDepsLeadingTo(pkg: Pkg): Array<{
|
69 | name: string;
|
70 | version?: string;
|
71 | }>;
|
72 | countPathsToRoot(pkg: Pkg): number;
|
73 | toJSON(): DepGraphData;
|
74 | equals(other: DepGraph, options?: { compareRoot?: boolean }): boolean;
|
75 | }
|
76 | ```
|
77 |
|
78 | ### `DepGraphData`
|
79 |
|
80 | A dep-graph can be serialised into the following format:
|
81 |
|
82 | ```typescript
|
83 | export interface DepGraphData {
|
84 | schemaVersion: string;
|
85 | pkgManager: {
|
86 | name: string;
|
87 | version?: string;
|
88 | repositories?: Array<{
|
89 | alias: string;
|
90 | }>;
|
91 | };
|
92 | pkgs: Array<{
|
93 | id: string;
|
94 | info: {
|
95 | name: string;
|
96 | version?: string;
|
97 | };
|
98 | }>;
|
99 | graph: {
|
100 | rootNodeId: string;
|
101 | nodes: Array<{
|
102 | nodeId: string;
|
103 | pkgId: string;
|
104 | info?: {
|
105 | versionProvenance?: {
|
106 | type: string;
|
107 | location: string;
|
108 | property?: {
|
109 | name: string;
|
110 | };
|
111 | },
|
112 | labels?: {
|
113 | [key: string]: string | undefined;
|
114 | };
|
115 | };
|
116 | deps: Array<{
|
117 | nodeId: string;
|
118 | }>;
|
119 | }>;
|
120 | };
|
121 | }
|
122 | ```
|
123 |
|
124 | ### `createFromJSON`
|
125 |
|
126 | `DepGraphData` can be used to construct a `DepGraph` instance using `createFromJSON`
|
127 |
|
128 | ### `DepGraphBuilder`
|
129 | `DepGraphBuilder` is used to create new `DepGraph` instances by adding packages and their connections.
|
130 |
|
131 | ```typescript
|
132 | /**
|
133 | * Instantiates build for given package manager
|
134 | *
|
135 | * @param pkgManager - package manager for which dependcy graph is created
|
136 | * @param rootPkg - root package information
|
137 | *
|
138 | */
|
139 | public constructor(pkgManager: types.PkgManager, rootPkg?: types.PkgInfo)
|
140 |
|
141 | /**
|
142 | * Adds node to the graph. Every node represents logical instance of the package in the dependency graph.
|
143 | *
|
144 | * @param pkgInfo - name and version of the package
|
145 | * @param nodeId - identifier for node in the graph, e.g. `package@version`.
|
146 | * Must uniquely identify this "instance" of the package in the graph,
|
147 | * so may need to be more than `package@version` for many ecosystems.
|
148 | * If in doubt - ask a contributor!
|
149 | * @param nodeInfo - additional node info, e.g. for version provenance
|
150 | *
|
151 | */
|
152 | public addPkgNode(pkgInfo: types.PkgInfo, nodeId: string, nodeInfo?: types.NodeInfo)
|
153 |
|
154 | /**
|
155 | * Makes a connection between parent and its dependency.
|
156 | *
|
157 | * @param parentNodeId - id of the parent node
|
158 | * @param depNodeId - id of the dependency node
|
159 | *
|
160 | */
|
161 | public connectDep(parentNodeId: string, depNodeId: string)
|
162 |
|
163 | /**
|
164 | * Creates an instance of DepGraph
|
165 | *
|
166 | * @return DepGraph instance built from provided packages and their connections
|
167 | *
|
168 | */
|
169 | public build(): types.DepGraph
|
170 |
|
171 | ```
|
172 | ### The `legacy` module
|
173 |
|
174 | A `DepTree` is a legacy structure used by the Snyk CLI to represent dependency trees. Conversion functions in the `legacy` module ease the gradual migration of code that relies on the legacy format.
|
175 |
|
176 | #### Legacy `DepTree`
|
177 |
|
178 | A `DepTree` is a recursive structure that is quite similar to the output of `npm list --json`, and (omitting some details) looks like:
|
179 |
|
180 | ```typescript
|
181 | interface DepTree {
|
182 | name: string;
|
183 | version: string;
|
184 | dependencies?: {
|
185 | [depName: string]: DepTree
|
186 | };
|
187 | }
|
188 | ```
|
189 |
|
190 | The `legacy` conversion functions aim to maintain extra data that might be attached to the dep-tree and is dependant upon in code that wasn't yet updated to use solely dep-graphs:
|
191 | * `targetOS` which exists on tree roots for Docker scans
|
192 | * `versionProvenance` which might exist on the nodes of maven trees, storing information about the source manifest that caused the specfic version to be resolved
|