├── .gitattributes ├── cli ├── .gitignore ├── src │ ├── update │ │ ├── hello.txt │ │ ├── hello.txt.signature │ │ ├── mod.rs │ │ └── unsupported.rs │ ├── auth │ │ ├── mod.rs │ │ ├── auth_callback_template.html │ │ └── jwt.rs │ ├── commands │ │ ├── find_dependency_files.rs │ │ ├── status.rs │ │ ├── mod.rs │ │ ├── packages.rs │ │ ├── firewall.rs │ │ └── sandbox.rs │ ├── lib.rs │ ├── dirs.rs │ ├── print.rs │ └── vulnreach.rs ├── tests │ ├── end_to_end │ │ └── mod.rs │ ├── fixtures │ │ └── extensions │ │ │ ├── args │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ │ │ ├── run-count │ │ │ ├── PhylumExt.toml │ │ │ └── main.ts │ │ │ ├── sample │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ │ │ ├── ping │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ │ │ ├── symlink │ │ │ ├── main.ts │ │ │ ├── symlink_me.ts │ │ │ └── PhylumExt.toml │ │ │ ├── module-import │ │ │ ├── successful │ │ │ │ ├── local_module.ts │ │ │ │ ├── not_typescript.js │ │ │ │ ├── PhylumExt.toml │ │ │ │ └── main.ts │ │ │ └── fail-local │ │ │ │ ├── main.ts │ │ │ │ └── PhylumExt.toml │ │ │ ├── api │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ │ │ └── permissions │ │ │ ├── correct-net-perms │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ │ │ ├── incorrect-net-perms │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ │ │ ├── incorrect-read-perms │ │ │ ├── PhylumExt.toml │ │ │ └── main.ts │ │ │ ├── correct-run-perms │ │ │ ├── PhylumExt.toml │ │ │ └── main.ts │ │ │ └── correct-read-perms │ │ │ ├── main.ts │ │ │ └── PhylumExt.toml │ ├── integration.rs │ ├── config.rs │ ├── parse.rs │ └── extensions │ │ └── module_loader.rs ├── js │ ├── api_version.js │ └── main.js └── build.rs ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── dependabot.yml └── workflows │ └── cargo-update.yml ├── tests └── fixtures │ ├── lock_generate_csproj │ ├── .gitignore │ ├── Program.cs │ └── lock_generate_csproj.csproj │ ├── yarn-v1.empty.lock │ ├── Gemfile.empty.lock │ ├── packages.config │ ├── yarn-v1.simple.lock │ ├── dev-requirements.txt │ ├── yarn-bun.lock │ ├── gradle.lockfile │ ├── yarn-v1.lock.bad │ ├── pnpm-lock-v5.yaml │ ├── nested_bom.json │ ├── go.mod │ ├── Pipfile │ ├── requirements-unlocked.txt │ ├── Gemfile.lock │ ├── requirements-locked.txt │ ├── locator.spdx │ ├── packages.lock.json │ └── sample.csproj ├── doc_templates ├── default.md ├── phylum_org_unlink.md ├── phylum_group_member_list.md ├── phylum_org_member_list.md ├── phylum_org_member_add.md ├── phylum_group_member_add.md ├── phylum_org_member_remove.md ├── phylum_group_member_remove.md ├── phylum_auth_token.md ├── phylum_uninstall.md ├── phylum_org_list.md ├── phylum_auth_revoke-token.md ├── phylum_org_link.md ├── phylum_project_create.md ├── phylum_extension_new.md ├── phylum_project_link.md ├── phylum_auth_set-token.md ├── phylum_extension_run.md ├── phylum_group_list.md ├── phylum_package.md ├── phylum_extension_install.md ├── phylum_init.md ├── phylum_group_create.md ├── phylum_project_list.md ├── phylum_group_delete.md ├── phylum_firewall_log.md ├── phylum_history.md ├── phylum_parse.md ├── phylum_analyze.md └── README.md ├── extensions ├── duplicates │ ├── PhylumExt.toml │ ├── README.md │ └── main.ts ├── README.md ├── find-permissions │ ├── PhylumExt.toml │ └── README.md ├── npm │ ├── PhylumExt.toml │ └── README.md ├── cargo │ ├── PhylumExt.toml │ └── README.md ├── bundle │ ├── PhylumExt.toml │ └── README.md ├── yarn │ └── PhylumExt.toml ├── poetry │ ├── fixtures │ │ └── pyproject.toml │ └── PhylumExt.toml └── pip │ └── PhylumExt.toml ├── clap_markdown ├── README.md └── Cargo.toml ├── .phylum_project ├── vulnreach_types ├── Cargo.lock ├── Cargo.toml └── src │ └── lib.rs ├── xtask ├── fixtures │ └── test-project │ │ ├── package.json │ │ └── yarn.lock ├── Cargo.toml └── src │ ├── gencomp.rs │ ├── main.rs │ └── gendocs.rs ├── .editorconfig ├── docs ├── commands │ ├── phylum_version.md │ ├── phylum_ping.md │ ├── phylum_extension_list.md │ ├── phylum_update.md │ ├── phylum_auth_status.md │ ├── phylum_extension_uninstall.md │ ├── phylum_status.md │ ├── phylum_firewall.md │ ├── phylum_auth_register.md │ ├── phylum_auth_list-tokens.md │ ├── phylum_org_unlink.md │ ├── phylum_project_delete.md │ ├── phylum_auth_login.md │ ├── phylum_org.md │ ├── phylum_exception.md │ ├── phylum_auth_create-token.md │ ├── phylum_org_member.md │ ├── phylum_group.md │ ├── phylum_org_member_list.md │ ├── phylum_org_member_add.md │ ├── phylum_group_member_list.md │ ├── phylum_group_member_add.md │ ├── phylum_org_member_remove.md │ ├── phylum_project_status.md │ ├── phylum_auth_token.md │ ├── phylum_org_link.md │ ├── phylum_group_member_remove.md │ ├── phylum_uninstall.md │ ├── phylum_exception_list.md │ ├── phylum_extension_new.md │ ├── phylum_org_list.md │ ├── phylum_extension.md │ ├── phylum_group_member.md │ ├── phylum_auth_revoke-token.md │ ├── phylum_project.md │ ├── phylum_project_update.md │ ├── phylum_group_list.md │ ├── phylum_auth_set-token.md │ ├── phylum_project_link.md │ ├── phylum_extension_run.md │ ├── phylum_auth.md │ ├── phylum_group_create.md │ ├── phylum_group_delete.md │ ├── phylum_project_create.md │ ├── phylum_project_list.md │ ├── phylum_extension_install.md │ ├── phylum_history.md │ ├── phylum_exception_add.md │ ├── phylum_exception_remove.md │ ├── phylum.md │ ├── phylum_init.md │ ├── phylum_package.md │ ├── phylum_parse.md │ ├── phylum_firewall_log.md │ └── phylum_analyze.md ├── alternate_install.md ├── extensions │ ├── extension_api.md │ ├── extension_quickstart.md │ ├── extension_rest_api.md │ └── extension_overview.md ├── analyzing_dependencies.md ├── supported_lockfiles.md └── quickstart.md ├── lockfile_generator ├── Cargo.toml └── src │ ├── dotnet.rs │ ├── go.rs │ ├── bundler.rs │ ├── pipenv.rs │ ├── poetry.rs │ ├── maven.rs │ ├── cargo.rs │ ├── yarn.rs │ └── pnpm.rs ├── deno.json ├── SECURITY.md ├── scripts └── signing-key.pub ├── rustfmt.toml ├── .gitignore ├── phylum_project └── Cargo.toml ├── Cargo.toml ├── lockfile ├── Cargo.toml └── src │ └── parsers │ ├── gradle_dep.rs │ ├── mod.rs │ └── go_sum.rs ├── bump_version.sh └── tag.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /cli/.gitignore: -------------------------------------------------------------------------------- 1 | src/bin/completions 2 | -------------------------------------------------------------------------------- /cli/src/update/hello.txt: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /cli/tests/end_to_end/mod.rs: -------------------------------------------------------------------------------- 1 | mod extension; 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @phylum-dev/user-components 2 | -------------------------------------------------------------------------------- /tests/fixtures/lock_generate_csproj/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | -------------------------------------------------------------------------------- /doc_templates/default.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/args/main.ts: -------------------------------------------------------------------------------- 1 | console.log(Deno.args); 2 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/run-count/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "run-count" 2 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/sample/main.ts: -------------------------------------------------------------------------------- 1 | console.log("Hello, World!"); 2 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/ping/main.ts: -------------------------------------------------------------------------------- 1 | Deno.core.print("Hello, world!\n"); 2 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/symlink/main.ts: -------------------------------------------------------------------------------- 1 | import { foo } from "./symlink.ts" 2 | 3 | foo() 4 | -------------------------------------------------------------------------------- /cli/src/update/hello.txt.signature: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phylum-dev/cli/HEAD/cli/src/update/hello.txt.signature -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/symlink/symlink_me.ts: -------------------------------------------------------------------------------- 1 | export function foo() { 2 | console.log("I am symlinked") 3 | } 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/args/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "args" 2 | description = "Print deno args" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/module-import/successful/local_module.ts: -------------------------------------------------------------------------------- 1 | export function something(): number { 2 | return 12345 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/yarn-v1.empty.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/sample/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "sample" 2 | description = "This extension does a thing" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/module-import/fail-local/main.ts: -------------------------------------------------------------------------------- 1 | import * as malicious from "../module-import-success/main.ts" 2 | 3 | malicious() 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/ping/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "ping" 2 | description = "This extension should be filtered out" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/symlink/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "symlink" 2 | description = "Test symlink related behavior" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /extensions/duplicates/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "duplicates" 2 | description = "List duplicate dependencies" 3 | 4 | [permissions] 5 | read = ["./"] 6 | -------------------------------------------------------------------------------- /tests/fixtures/lock_generate_csproj/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | Console.WriteLine("Hello, World!"); 3 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/module-import/successful/not_typescript.js: -------------------------------------------------------------------------------- 1 | export function notTypescript() { 2 | console.log("Regular .js files are supported") 3 | } 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/api/main.ts: -------------------------------------------------------------------------------- 1 | const lockfile = await Phylum.parseDependencyFile("../tests/fixtures/poetry.lock", "poetry"); 2 | console.log(lockfile.packages.length); 3 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/module-import/successful/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "module-import-success" 2 | description = "Test: successful import of modules" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/module-import/fail-local/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "module-import-fail-local" 2 | description = "Test: fail to import disallowed local path" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/correct-net-perms/main.ts: -------------------------------------------------------------------------------- 1 | let response = await fetch('https://www.veracode.com') 2 | let release = await response.text() 3 | 4 | console.log(release) 5 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/incorrect-net-perms/main.ts: -------------------------------------------------------------------------------- 1 | let response = await fetch('https://veracode.com') 2 | let release = await response.json() 3 | 4 | console.log(release) 5 | -------------------------------------------------------------------------------- /tests/fixtures/Gemfile.empty.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | 5 | PLATFORMS 6 | x86_64-linux 7 | 8 | DEPENDENCIES 9 | 10 | BUNDLED WITH 11 | 2.4.13 12 | -------------------------------------------------------------------------------- /clap_markdown/README.md: -------------------------------------------------------------------------------- 1 | # clap_markdown 2 | 3 | The clap_markdown crate provides an interface for converting a `clap::Command` 4 | into markdown pages for the command and each of its subcommands. 5 | -------------------------------------------------------------------------------- /cli/js/api_version.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Available Phylum REST API versions. 3 | * 4 | * NOTE: This must match the type in `extensions/phylum.d.ts`. 5 | */ 6 | export const ApiVersion = { 7 | V0: "v0", 8 | }; 9 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/api/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "api" 2 | description = "Test phylum API injection" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | read = ["../tests/fixtures/poetry.lock"] 7 | -------------------------------------------------------------------------------- /doc_templates/phylum_org_unlink.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Clear the default organization used by the CLI 9 | $ phylum org unlink 10 | ``` 11 | -------------------------------------------------------------------------------- /.phylum_project: -------------------------------------------------------------------------------- 1 | id: d84b0bb3-fb64-43cb-a473-dc96b54db662 2 | name: cli 3 | created_at: 2024-09-20T12:27:33.762071-05:00 4 | group_name: integrations 5 | depfiles: 6 | - path: ./Cargo.lock 7 | type: cargo 8 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/incorrect-read-perms/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "incorrect-read-perms" 2 | description = "An extension with incorrectly specified permissions" 3 | entry_point = "main.ts" 4 | -------------------------------------------------------------------------------- /vulnreach_types/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "vulnreach_types" 7 | version = "0.1.0" 8 | -------------------------------------------------------------------------------- /extensions/README.md: -------------------------------------------------------------------------------- 1 | # Phylum Extensions 2 | 3 | This directory contains all official Phylum CLI extensions. To get started just 4 | pick the extension you want to install and follow the instructions in their 5 | README. 6 | -------------------------------------------------------------------------------- /xtask/fixtures/test-project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-project", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "react": "^17.0.2" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /doc_templates/phylum_group_member_list.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # List all group members for the `sample` group 9 | $ phylum group member --group sample list 10 | ``` 11 | -------------------------------------------------------------------------------- /doc_templates/phylum_org_member_list.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # List all organization members for the `sample` organization 9 | $ phylum org -o sample member list 10 | ``` 11 | -------------------------------------------------------------------------------- /extensions/find-permissions/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "find-permissions" 2 | description = "Find required sandbox permissions" 3 | 4 | [permissions] 5 | read = true 6 | write = true 7 | run = true 8 | env = true 9 | net = true 10 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/correct-run-perms/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "correct-run-perms" 2 | description = "An extension with correctly specified permissions" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | run = ["echo"] 7 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/incorrect-read-perms/main.ts: -------------------------------------------------------------------------------- 1 | import { walk, exists } from "https://deno.land/std@0.143.0/fs/mod.ts" 2 | 3 | let data = new TextDecoder().decode(await Deno.readFile("/tmp/passwd")) 4 | console.log(data) 5 | -------------------------------------------------------------------------------- /cli/tests/integration.rs: -------------------------------------------------------------------------------- 1 | pub mod common; 2 | 3 | #[cfg(feature = "end-to-end-tests")] 4 | mod end_to_end; 5 | 6 | mod config; 7 | #[cfg(feature = "extensions")] 8 | mod extensions; 9 | mod parse; 10 | #[cfg(unix)] 11 | mod sandbox; 12 | -------------------------------------------------------------------------------- /cli/js/main.js: -------------------------------------------------------------------------------- 1 | import * as Api from "ext:phylum_api/api.js"; 2 | import { ApiVersion } from "ext:phylum_api/api_version.js"; 3 | 4 | globalThis.Phylum ??= {}; 5 | Object.assign(globalThis.Phylum, Api); 6 | globalThis.Phylum.ApiVersion = ApiVersion; 7 | -------------------------------------------------------------------------------- /doc_templates/phylum_org_member_add.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Add user `demo@veracode.com` to the `sample` organization 9 | $ phylum org -o sample member add demo@veracode.com 10 | ``` 11 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/correct-net-perms/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "correct-net-perms" 2 | description = "An extension with correctly specified permissions" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | net = ["www.veracode.com"] 7 | -------------------------------------------------------------------------------- /doc_templates/phylum_group_member_add.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Add user `demo@veracode.com` to the `sample` group 9 | $ phylum group member --group sample add demo@veracode.com 10 | ``` 11 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/correct-read-perms/main.ts: -------------------------------------------------------------------------------- 1 | import { walk, exists } from "https://deno.land/std@0.143.0/fs/mod.ts" 2 | 3 | let data = new TextDecoder().decode(await Deno.readFile("./correct-read-perms/main.ts")) 4 | console.log(data) 5 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/incorrect-net-perms/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "incorrect-net-perms" 2 | description = "An extension with incorrectly specified permissions" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | net = ["veracode.dev"] 7 | -------------------------------------------------------------------------------- /doc_templates/phylum_org_member_remove.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Remove user `demo@veracode.com` from the `sample` organization 9 | $ phylum org -o sample member remove demo@veracode.com 10 | ``` 11 | -------------------------------------------------------------------------------- /extensions/npm/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "npm" 2 | description = "NPM package manager hooks" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | write = ["~/.npm", "./"] 7 | read = true 8 | run = true 9 | net = true 10 | unsandboxed_run = ["npm"] 11 | -------------------------------------------------------------------------------- /doc_templates/phylum_group_member_remove.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Remove user `demo@veracode.com` from the `sample` group 9 | $ phylum group member --group sample remove demo@veracode.com 10 | ``` 11 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/correct-read-perms/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "correct-read-perms" 2 | description = "An extension with correctly specified permissions" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | read = ["./correct-read-perms/main.ts"] 7 | -------------------------------------------------------------------------------- /doc_templates/phylum_auth_token.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Output long-lived refresh token 9 | $ phylum auth token 10 | 11 | # Output short-lived bearer token 12 | $ phylum auth token --bearer 13 | ``` 14 | -------------------------------------------------------------------------------- /extensions/cargo/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "cargo" 2 | description = "Cargo package manager hooks" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | write = ["./", "~/.cargo"] 7 | read = true 8 | run = true 9 | net = true 10 | unsandboxed_run = ["cargo"] 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.{rs,toml}] 10 | indent_style = space 11 | indent_size = 4 12 | 13 | [*.md] 14 | max_line_length = 80 15 | -------------------------------------------------------------------------------- /doc_templates/phylum_uninstall.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Remove installed phylum binary and data files 9 | $ phylum uninstall 10 | 11 | # Remove all installed phylum files 12 | $ phylum uninstall --purge 13 | ``` 14 | -------------------------------------------------------------------------------- /extensions/bundle/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "bundle" 2 | description = "Bundler package manager hooks" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | write = ["./", "/tmp", "~/.bundle"] 7 | read = true 8 | run = true 9 | net = true 10 | unsandboxed_run = ["bundle"] 11 | -------------------------------------------------------------------------------- /cli/src/auth/mod.rs: -------------------------------------------------------------------------------- 1 | pub use ip_addr_ext::*; 2 | pub use oidc::*; 3 | pub use server::*; 4 | 5 | mod ip_addr_ext; 6 | pub mod jwt; 7 | mod oidc; 8 | mod server; 9 | 10 | pub fn is_locksmith_token(token: impl AsRef) -> bool { 11 | token.as_ref().starts_with("ph0_") 12 | } 13 | -------------------------------------------------------------------------------- /vulnreach_types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vulnreach_types" 3 | version = "0.1.0" 4 | authors = ["Veracode, Inc. "] 5 | edition = "2021" 6 | rust-version = "1.85.0" 7 | 8 | [dependencies] 9 | serde = { version = "1.0.152", features = ["derive"] } 10 | -------------------------------------------------------------------------------- /doc_templates/phylum_org_list.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # List all organizations the user is a member of 9 | $ phylum org list 10 | 11 | # List all organizations the user is a member of with json output 12 | $ phylum org list --json 13 | ``` 14 | -------------------------------------------------------------------------------- /doc_templates/phylum_auth_revoke-token.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Interactively select tokens to revoke. 9 | $ phylum auth revoke-token 10 | 11 | # Revoke tokens "token1" and "token2". 12 | $ phylum auth revoke-token "token1" "token2" 13 | ``` 14 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/module-import/successful/main.ts: -------------------------------------------------------------------------------- 1 | import { crypto } from "https://deno.land/std@0.143.0/crypto/mod.ts" 2 | import { something } from "./local_module.ts" 3 | import { notTypescript } from "./not_typescript.js" 4 | 5 | let value = something() 6 | console.log(`I should contain ${value}`) 7 | 8 | -------------------------------------------------------------------------------- /doc_templates/phylum_org_link.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Interactively select an organization for all future operations 9 | $ phylum org link 10 | 11 | # Set `sample` as the default organization for all future operations 12 | $ phylum org link sample 13 | ``` 14 | -------------------------------------------------------------------------------- /doc_templates/phylum_project_create.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Create a new project named `sample` 9 | $ phylum project create sample 10 | 11 | # Create a new project named `sample` owned by the group `sGroup` 12 | $ phylum project create -g sGroup sample 13 | ``` 14 | -------------------------------------------------------------------------------- /extensions/yarn/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "yarn" 2 | description = "yarn package manager hooks" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | write = ["/tmp", "~/.cache/node", "~/.cache/yarn", "~/.yarn", "./", "~/Library/Caches/Yarn"] 7 | read = true 8 | run = true 9 | net = true 10 | unsandboxed_run = ["yarn"] 11 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/permissions/correct-run-perms/main.ts: -------------------------------------------------------------------------------- 1 | let cmd = Phylum.runSandboxed({ 2 | cmd: 'echo', 3 | args: ['hello'], 4 | stdout: 'piped', 5 | stderr: 'piped', 6 | exceptions: { 7 | run: ['echo'], 8 | }, 9 | }); 10 | 11 | await Deno.stdout.write(new TextEncoder().encode(cmd.stdout)); 12 | -------------------------------------------------------------------------------- /doc_templates/phylum_extension_new.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Details 6 | 7 | This command will create a "Hello, World!" extension skeleton that can be 8 | modified for a specific use. See the [Extension Quickstart] guide for more 9 | detail. 10 | 11 | [Extension Quickstart]: ../extensions/extension_quickstart.md 12 | -------------------------------------------------------------------------------- /doc_templates/phylum_project_link.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Link current folder to an existing project named `sample` 9 | $ phylum project link sample 10 | 11 | # Link current folder to an existing project named `sample` owned by the group `sGroup` 12 | $ phylum project link -g sGroup sample 13 | ``` 14 | -------------------------------------------------------------------------------- /cli/tests/fixtures/extensions/run-count/main.ts: -------------------------------------------------------------------------------- 1 | function runCount() { 2 | try { 3 | const c = parseInt(localStorage.getItem("runCount") ?? "0", 10); 4 | localStorage.setItem("runCount", `${c+1}`); 5 | return c; 6 | } catch(_e) { 7 | // Local Storage Disabled 8 | return -1; 9 | } 10 | } 11 | 12 | console.log(`Run Count: ${runCount()}`); 13 | -------------------------------------------------------------------------------- /doc_templates/phylum_auth_set-token.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Supply the token directly on the command line 9 | $ phylum auth set-token ph0_UyqKk8yRmuO4gRx52os3obQevBluJTGsepQw0bLRmX0 10 | 11 | # Supply the token on stdin 12 | $ phylum auth set-token 13 | ph0_UyqKk8yRmuO4gRx52os3obQevBluJTGsepQw0bLRmX0 14 | ``` 15 | -------------------------------------------------------------------------------- /doc_templates/phylum_extension_run.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Details 6 | 7 | The extension will be run without prior installation. 8 | 9 | The first set of options are for the `run` command. The second set of options 10 | are for the extension. 11 | 12 | ## Examples 13 | 14 | ```sh 15 | phylum extension run --yes ./my-extension --help 16 | ``` 17 | -------------------------------------------------------------------------------- /doc_templates/phylum_group_list.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # List all groups the user is a member of 9 | $ phylum group list 10 | 11 | # List all groups the user is a member of with json output 12 | $ phylum group list --json 13 | 14 | # List all groups for the `test` organization 15 | $ phylum group list --org test 16 | ``` 17 | -------------------------------------------------------------------------------- /doc_templates/phylum_package.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Details 6 | 7 | If the requested package has not yet been analyzed by Phylum, it will 8 | automatically be submitted for [processing]. 9 | 10 | [processing]: ../../knowledge_base/faq.md 11 | 12 | ## Examples 13 | 14 | ```sh 15 | # Query specific package details 16 | $ phylum package -t npm axios 0.19.0 17 | ``` 18 | -------------------------------------------------------------------------------- /extensions/poetry/fixtures/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "fixture" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Veracode, Inc. "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.10" 9 | 10 | [tool.poetry.dev-dependencies] 11 | 12 | [build-system] 13 | requires = ["poetry-core>=1.0.0"] 14 | build-backend = "poetry.core.masonry.api" 15 | -------------------------------------------------------------------------------- /tests/fixtures/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ## Checklist 3 | 4 | - [ ] Does this PR have an associated issue (i.e., `closes #` in description above)? 5 | - [ ] Have you ensured that you have met the expected acceptance criteria? 6 | - [ ] Have you created sufficient tests? 7 | - [ ] Have you updated all affected documentation? 8 | - [ ] Have you updated CHANGELOG.md (or extensions/CHANGELOG.md), if applicable 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New Feature/Component 3 | about: Create a new feature/component request for a user story 4 | title: '' 5 | labels: enhancement, needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Overview 11 | A concise description of the new feature component. 12 | 13 | # Acceptance Criteria 14 | - [ ] Outputs XYZ when ... 15 | - [ ] New endpoint ABC exists ... 16 | -------------------------------------------------------------------------------- /tests/fixtures/yarn-v1.simple.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@yarnpkg/lockfile@^1.1.0": 6 | version "1.1.0" 7 | resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" 8 | integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== 9 | -------------------------------------------------------------------------------- /clap_markdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clap_markdown" 3 | description = "Markdown documentation generator for clap" 4 | repository = "https://github.com/phylum-dev/cli" 5 | categories = ["command-line-interface"] 6 | keywords = ["clap", "cli", "generate", "markdown"] 7 | license = "GPL-3.0-or-later" 8 | rust-version = "1.85.0" 9 | version = "0.1.0" 10 | edition = "2021" 11 | 12 | [dependencies] 13 | clap = "4.0.26" 14 | -------------------------------------------------------------------------------- /cli/src/commands/find_dependency_files.rs: -------------------------------------------------------------------------------- 1 | //! `phylum find-dependency-files` subcommand. 2 | 3 | use crate::commands::{CommandResult, ExitCode}; 4 | 5 | /// Handle `phylum find-dependency-files` subcommand. 6 | pub fn handle_command() -> CommandResult { 7 | let depfiles = phylum_lockfile::DepFiles::find_at("."); 8 | let json = serde_json::to_string(&depfiles)?; 9 | println!("{json}"); 10 | Ok(ExitCode::Ok) 11 | } 12 | -------------------------------------------------------------------------------- /doc_templates/phylum_extension_install.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Details 6 | 7 | The extension will be installed under `$XDG_DATA_HOME/phylum/extensions/`. 8 | If `$XDG_DATA_HOME` is not set, it will default to `$HOME/.local/share/phylum/extensions/`. 9 | 10 | Once installed, the extension will be accessible via the Phylum CLI: 11 | 12 | ```sh 13 | phylum [OPTIONS]... 14 | ``` 15 | -------------------------------------------------------------------------------- /cli/src/update/mod.rs: -------------------------------------------------------------------------------- 1 | //! Self-update functions 2 | //! 3 | //! Self-update is currently supported on macos and Linux. All other platforms 4 | //! are unsupported and will display an error when `phylum update` is run. For 5 | //! Windows support, see issue #221 6 | 7 | #[cfg(unix)] 8 | pub use self::unix::*; 9 | #[cfg(not(unix))] 10 | pub use self::unsupported::*; 11 | 12 | #[cfg(unix)] 13 | mod unix; 14 | #[cfg(not(unix))] 15 | mod unsupported; 16 | -------------------------------------------------------------------------------- /cli/src/update/unsupported.rs: -------------------------------------------------------------------------------- 1 | //! Dummy functions for platforms where self-update is unsupported 2 | 3 | /// Check if a newer version of the client is available 4 | pub async fn needs_update(_prerelease: bool) -> bool { 5 | false 6 | } 7 | 8 | /// Perform a self-update to the latest version 9 | pub async fn do_update(_prerelease: bool, _ignore_certs: bool) -> anyhow::Result { 10 | anyhow::bail!("Self-update not supported on this platform") 11 | } 12 | -------------------------------------------------------------------------------- /doc_templates/phylum_init.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Interactively initialize the Phylum project. 9 | $ phylum init 10 | 11 | # Create the `demo` project with a yarn lockfile and no associated group. 12 | $ phylum init --dependency-file yarn.lock --type yarn demo 13 | 14 | # Create the `demo` project in the `sample` group of the `test` organization. 15 | $ phylum init --org test --group sample demo 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/commands/phylum_version.md: -------------------------------------------------------------------------------- 1 | # phylum version 2 | 3 | Display application version 4 | 5 | ```sh 6 | Usage: phylum version [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | -------------------------------------------------------------------------------- /lockfile_generator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lockfile_generator" 3 | version = "0.1.0" 4 | authors = ["Veracode, Inc. "] 5 | license = "GPL-3.0-or-later" 6 | edition = "2021" 7 | rust-version = "1.85.0" 8 | 9 | [dependencies] 10 | serde = { version = "1.0.163", features = ["derive"] } 11 | serde_json = "1.0.96" 12 | anyhow = "1.0.75" 13 | glob = "0.3.1" 14 | thiserror = "2.0.3" 15 | tempfile = "3.3.0" 16 | dunce = "1.0.5" 17 | -------------------------------------------------------------------------------- /cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use reqwest::Error; 2 | 3 | pub mod api; 4 | pub mod app; 5 | pub mod auth; 6 | pub mod commands; 7 | pub mod config; 8 | #[cfg(feature = "extensions")] 9 | pub mod deno; 10 | pub mod dirs; 11 | pub mod filter; 12 | pub mod format; 13 | pub mod fs_compare; 14 | pub mod permissions; 15 | pub mod print; 16 | pub mod spinner; 17 | #[cfg(test)] 18 | mod test; 19 | pub mod types; 20 | pub mod update; 21 | #[cfg(feature = "vulnreach")] 22 | pub mod vulnreach; 23 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": [ 4 | "./extensions/phylum.d.ts" 5 | ] 6 | }, 7 | "lint": { 8 | "include": [ 9 | "extensions/" 10 | ], 11 | "rules": { 12 | "exclude": [ 13 | "no-import-prefix" 14 | ] 15 | } 16 | }, 17 | "fmt": { 18 | "include": [ 19 | "extensions/" 20 | ], 21 | "exclude": [ 22 | "extensions/CHANGELOG.md" 23 | ] 24 | }, 25 | "lock": false 26 | } 27 | -------------------------------------------------------------------------------- /extensions/pip/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "pip" 2 | description = "pip package manager hooks" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | run = ["./", "/bin", "/usr/bin", "/usr/local/bin", "~/.pyenv", "/usr/share/pyenv"] 7 | write = ["./", "~/Library/Caches", "~/Library/Python", "~/.cache", "~/.local", "~/.pyenv", "/tmp"] 8 | read = ["~/Library/Caches", "~/Library/Python", "~/.cache", "~/.local", "/tmp", "/etc/passwd"] 9 | net = true 10 | unsandboxed_run = ["pip3"] 11 | -------------------------------------------------------------------------------- /doc_templates/phylum_group_create.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Create a new group named `sample` 9 | $ phylum group create sample 10 | 11 | # Create a group `sample` under the `test` organization 12 | $ phylum group create --org test sample 13 | 14 | # Make `test` the default organization for all operations, 15 | # then create a new group `sample` under it. 16 | $ phylum org link test 17 | $ phylum group create sample 18 | ``` 19 | -------------------------------------------------------------------------------- /doc_templates/phylum_project_list.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # List all existing projects 9 | $ phylum project list 10 | 11 | # List all existing projects with json output 12 | $ phylum project list --json 13 | 14 | # List all existing projects for the `sample` group 15 | $ phylum project list -g sample 16 | 17 | # List all existing projects for the `demo` organization 18 | $ phylum project list --organization demo 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/commands/phylum_ping.md: -------------------------------------------------------------------------------- 1 | # phylum ping 2 | 3 | Ping the remote system to verify it is available 4 | 5 | ```sh 6 | Usage: phylum ping [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | -------------------------------------------------------------------------------- /doc_templates/phylum_group_delete.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Delete an existing group named `sample` 9 | $ phylum group delete sample 10 | 11 | # Delete the group `sample` from the `test` organization 12 | $ phylum group delete --org test sample 13 | 14 | # Make `test` the default organization for all operations, 15 | # then delete the group `sample` from it. 16 | $ phylum org link test 17 | $ phylum group delete sample 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/commands/phylum_extension_list.md: -------------------------------------------------------------------------------- 1 | # phylum extension list 2 | 3 | List installed extensions 4 | 5 | ```sh 6 | Usage: phylum extension list [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | -------------------------------------------------------------------------------- /docs/commands/phylum_update.md: -------------------------------------------------------------------------------- 1 | # phylum update 2 | 3 | Update to the latest release of the Phylum CLI 4 | 5 | ```sh 6 | Usage: phylum update [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_status.md: -------------------------------------------------------------------------------- 1 | # phylum auth status 2 | 3 | Return the current authentication status 4 | 5 | ```sh 6 | Usage: phylum auth status [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | At Veracode, security is job number one. We consider security in everything we 4 | do to ensure the trustworthiness of the tools and services we provide. 5 | 6 | ## Reporting a Vulnerability 7 | 8 | We love coordinated disclosure! 9 | Please email [grc@veracode.com](mailto:grc@veracode.com) to start a conversation! 10 | We'll coordinate a secure communication mechanism first, then evaluate the 11 | reported issue(s) and keep you apprised each step of the way. 12 | -------------------------------------------------------------------------------- /tests/fixtures/dev-requirements.txt: -------------------------------------------------------------------------------- 1 | # This is an example of a pip manifest file that does not fit the expected naming convention of: 2 | # 3 | # requirements*.txt 4 | # 5 | # Pip manifest files can be named *anything* and they are therefore hard to identify. 6 | # Pip lockfiles can also be named *anything* but they will at least get picked up 7 | # by the fallback approach of attempting to parse with all known parsers (assuming 8 | # a "true" lockfile with no loose requirement entries). 9 | pyyaml 10 | -------------------------------------------------------------------------------- /tests/fixtures/yarn-bun.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | # bun ./bun.lockb --hash: F7B8FCD3BF239CDC-d0456036d0c49935-8CE7F7B066308833-a6ad583c4212f008 4 | 5 | 6 | "@aashutoshrathi/word-wrap@^1.2.3": 7 | version "1.2.6" 8 | resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" 9 | integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== 10 | 11 | 12 | -------------------------------------------------------------------------------- /scripts/signing-key.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyGgvuy6CWSgJuhKY8oVz 3 | 42udH1F2yIlaBoxAdQFuY2zxPSSpK9zv34B7m0JekuC5WCYfW0gS2Z8Ryu2RVdQh 4 | 7DXvQb7qwzZT0H11K9Pw8hIHBvZPM+d61GWgWDc3k/rFwMmqd+kytVZy0mVxNdv4 5 | P2qvy6BNaiUI7yoB1ahR/6klfkPit0X7pkK9sTHwW+/WcYitTQKnEnRzA3q8EmA7 6 | rbU/sFEypzBA3C3qNJZyKSwy47kWXhC4xXUS2NXvew4FoVU6ybMoeDApwsx1AgTu 7 | CPPnPlCwuCIyUPezCP5XYczuHfaWeuwArlwdJFSUpMTc+SqO6REKgL9yvpqsO5Ia 8 | sQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | format_code_in_doc_comments = true 2 | group_imports = "StdExternalCrate" 3 | match_block_trailing_comma = true 4 | condense_wildcard_suffixes = true 5 | use_field_init_shorthand = true 6 | normalize_doc_attributes = true 7 | overflow_delimited_expr = true 8 | imports_granularity = "Module" 9 | use_small_heuristics = "Max" 10 | normalize_comments = true 11 | reorder_impl_items = true 12 | use_try_shorthand = true 13 | newline_style = "Unix" 14 | format_strings = true 15 | wrap_comments = true 16 | -------------------------------------------------------------------------------- /doc_templates/phylum_firewall_log.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # Show logs for packages which failed analysis for the group `demo`. 9 | $ phylum firewall log demo --action AnalysisFailure 10 | 11 | # Show logs which were created after 2024 for the group `demo`. 12 | $ phylum firewall log demo --after 2024-01-01T00:00:0.0Z 13 | 14 | # Show logs for libc regardless of its version for the group `demo`. 15 | $ phylum firewall log demo --package pkg:cargo/libc 16 | ``` 17 | -------------------------------------------------------------------------------- /docs/commands/phylum_extension_uninstall.md: -------------------------------------------------------------------------------- 1 | # phylum extension uninstall 2 | 3 | Uninstall extension 4 | 5 | ```sh 6 | Usage: phylum extension uninstall [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 | 13 | ## Options 14 | 15 | `-o`, `--org` `` 16 |   Phylum organization 17 | 18 | `-v`, `--verbose`... 19 |   Increase the level of verbosity (the maximum is -vvv) 20 | 21 | `-q`, `--quiet`... 22 |   Reduce the level of verbosity (the maximum is -qq) 23 | 24 | `-h`, `--help` 25 |   Print help 26 | -------------------------------------------------------------------------------- /doc_templates/phylum_history.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Examples 6 | 7 | ```sh 8 | # List the last 30 analysis runs 9 | $ phylum history 10 | 11 | # View the analysis results of a historical job 12 | $ phylum history 338ea79f-0e82-4422-9769-4e583a84599f 13 | 14 | # View a list of analysis runs for the `sample` project 15 | $ phylum history --project sample 16 | 17 | # Show analysis runs for the `sample` project of the `demo` group under the `test` org 18 | $ phylum history --org test --group demo --project sample 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/commands/phylum_status.md: -------------------------------------------------------------------------------- 1 | # phylum status 2 | 3 | Get Phylum project details 4 | 5 | ```sh 6 | Usage: phylum status [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: File a new bug report 4 | title: '' 5 | labels: bug, needs triage 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Overview 11 | A clear and concise description of what the bug is. 12 | 13 | # How To Reproduce 14 | Steps to reproduce this behavior: 15 | 1. Go to '...' 16 | 2. Click on '...' 17 | 3. See error 18 | 19 | # Expected Behavior 20 | A clear and concise description of what you expected to happen. 21 | 22 | # Additional Context 23 | Add any other context about the issue here. 24 | -------------------------------------------------------------------------------- /docs/commands/phylum_firewall.md: -------------------------------------------------------------------------------- 1 | # phylum firewall 2 | 3 | Manage the package firewall 4 | 5 | ```sh 6 | Usage: phylum firewall [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum firewall log](./phylum_firewall_log.md) 26 | -------------------------------------------------------------------------------- /tests/fixtures/gradle.lockfile: -------------------------------------------------------------------------------- 1 | # This is a Gradle generated file for dependency locking. 2 | # Manual edits can break the build and are not advised. 3 | # This file is expected to be part of source control. 4 | com.google.code.findbugs:jsr305:1.3.9=classpath 5 | com.google.errorprone:error_prone_annotations:2.0.18=classpath 6 | com.google.guava:guava:23.3-jre=classpath 7 | 8 | com.google.j2objc:j2objc-annotations:1.1=classpath 9 | org.codehaus.mojo:animal-sniffer-annotations:1.14=classpath 10 | org.springframework:spring-core:5.2.15.RELEASE 11 | empty=runtimeClasspath 12 | -------------------------------------------------------------------------------- /extensions/poetry/PhylumExt.toml: -------------------------------------------------------------------------------- 1 | name = "poetry" 2 | description = "Poetry package manager hooks" 3 | entry_point = "main.ts" 4 | 5 | [permissions] 6 | run = ["./", "/bin", "/usr/bin", "~/.pyenv", "~/.local/bin/poetry", "~/Library/Application Support/pypoetry", "~/.local/share/pypoetry", "~/.local/pipx"] 7 | write = ["./", "~/.cache/pypoetry", "~/Library/Caches/pypoetry", "~/.pyenv"] 8 | read = ["./", "~/.cache/pypoetry", "~/Library/Caches/pypoetry", "~/.pyenv", "~/Library/Preferences/pypoetry", "~/.config/pypoetry", "/etc/passwd"] 9 | net = true 10 | unsandboxed_run = ["poetry"] 11 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_register.md: -------------------------------------------------------------------------------- 1 | # phylum auth register 2 | 3 | Register a new account 4 | 5 | ```sh 6 | Usage: phylum auth register [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-n`, `--token-name` `` 12 |   Unique name for the new token that will be created 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_list-tokens.md: -------------------------------------------------------------------------------- 1 | # phylum auth list-tokens 2 | 3 | List all tokens associated with the logged-in user 4 | 5 | ```sh 6 | Usage: phylum auth list-tokens [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | cli/target/ 5 | bindings/python/target/ 6 | 7 | # These are backup files generated by rustfmt 8 | **/*.rs.bk 9 | 10 | ## Vim ### 11 | # Swap 12 | [._]*.s[a-v][a-z] 13 | !*.svg # comment out if you don't need vector files 14 | [._]*.sw[a-p] 15 | [._]s[a-rt-v][a-z] 16 | [._]ss[a-gi-z] 17 | [._]sw[a-p] 18 | 19 | # Session 20 | Session.vim 21 | Sessionx.vim 22 | 23 | # Temporary 24 | .netrwhist 25 | *~ 26 | # Auto-generated tag files 27 | tags 28 | # Persistent undo 29 | [._]*.un~ 30 | # macOS 31 | **/.DS_Store 32 | -------------------------------------------------------------------------------- /cli/src/auth/auth_callback_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | {{}} 17 |
18 |
19 | You may close this window now 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_unlink.md: -------------------------------------------------------------------------------- 1 | # phylum org unlink 2 | 3 | Clear the configured default organization 4 | 5 | ```sh 6 | Usage: phylum org unlink [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Examples 24 | 25 | ```sh 26 | # Clear the default organization used by the CLI 27 | $ phylum org unlink 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/commands/phylum_project_delete.md: -------------------------------------------------------------------------------- 1 | # phylum project delete 2 | 3 | Delete a project 4 | 5 | ```sh 6 | Usage: phylum project delete [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Name of the project 13 | 14 | ## Options 15 | 16 | `-g`, `--group` `` 17 |   Group that owns the project 18 | 19 | `-o`, `--org` `` 20 |   Phylum organization 21 | 22 | `-v`, `--verbose`... 23 |   Increase the level of verbosity (the maximum is -vvv) 24 | 25 | `-q`, `--quiet`... 26 |   Reduce the level of verbosity (the maximum is -qq) 27 | 28 | `-h`, `--help` 29 |   Print help 30 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_login.md: -------------------------------------------------------------------------------- 1 | # phylum auth login 2 | 3 | Login to an existing account 4 | 5 | ```sh 6 | Usage: phylum auth login [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-r`, `--reauth` 12 |   Force a login prompt 13 | 14 | `-n`, `--token-name` `` 15 |   Unique name for the new token that will be created 16 | 17 | `-o`, `--org` `` 18 |   Phylum organization 19 | 20 | `-v`, `--verbose`... 21 |   Increase the level of verbosity (the maximum is -vvv) 22 | 23 | `-q`, `--quiet`... 24 |   Reduce the level of verbosity (the maximum is -qq) 25 | 26 | `-h`, `--help` 27 |   Print help 28 | -------------------------------------------------------------------------------- /tests/fixtures/yarn-v1.lock.bad: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@yarnpkg/lockfile@^1.1.0": 6 | resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" 7 | integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== 8 | 9 | ansi-regex@^5.0.0: 10 | version "5 11 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 12 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 13 | -------------------------------------------------------------------------------- /docs/commands/phylum_org.md: -------------------------------------------------------------------------------- 1 | # phylum org 2 | 3 | Manage organizations 4 | 5 | ```sh 6 | Usage: phylum org [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum org link](./phylum_org_link.md) 26 | * [phylum org list](./phylum_org_list.md) 27 | * [phylum org member](./phylum_org_member.md) 28 | * [phylum org unlink](./phylum_org_unlink.md) 29 | -------------------------------------------------------------------------------- /docs/commands/phylum_exception.md: -------------------------------------------------------------------------------- 1 | # phylum exception 2 | 3 | Manage analysis exceptions 4 | 5 | ```sh 6 | Usage: phylum exception [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum exception add](./phylum_exception_add.md) 26 | * [phylum exception list](./phylum_exception_list.md) 27 | * [phylum exception remove](./phylum_exception_remove.md) 28 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_create-token.md: -------------------------------------------------------------------------------- 1 | # phylum auth create-token 2 | 3 | Create a new API token 4 | 5 | ```sh 6 | Usage: phylum auth create-token [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Unique name to identify the new token 13 | 14 | ## Options 15 | 16 | `-e`, `--expiry` `` 17 |   Number of days the token will be valid 18 | 19 | `-o`, `--org` `` 20 |   Phylum organization 21 | 22 | `-v`, `--verbose`... 23 |   Increase the level of verbosity (the maximum is -vvv) 24 | 25 | `-q`, `--quiet`... 26 |   Reduce the level of verbosity (the maximum is -qq) 27 | 28 | `-h`, `--help` 29 |   Print help 30 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_member.md: -------------------------------------------------------------------------------- 1 | # phylum org member 2 | 3 | Manage organization members 4 | 5 | ```sh 6 | Usage: phylum org member [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum org member add](./phylum_org_member_add.md) 26 | * [phylum org member list](./phylum_org_member_list.md) 27 | * [phylum org member remove](./phylum_org_member_remove.md) 28 | -------------------------------------------------------------------------------- /docs/commands/phylum_group.md: -------------------------------------------------------------------------------- 1 | # phylum group 2 | 3 | Interact with user groups 4 | 5 | ```sh 6 | Usage: phylum group [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum group create](./phylum_group_create.md) 26 | * [phylum group delete](./phylum_group_delete.md) 27 | * [phylum group list](./phylum_group_list.md) 28 | * [phylum group member](./phylum_group_member.md) 29 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_member_list.md: -------------------------------------------------------------------------------- 1 | # phylum org member list 2 | 3 | List organization members 4 | 5 | ```sh 6 | Usage: phylum org member list [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce member list in json format (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Examples 27 | 28 | ```sh 29 | # List all organization members for the `sample` organization 30 | $ phylum org -o sample member list 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_member_add.md: -------------------------------------------------------------------------------- 1 | # phylum org member add 2 | 3 | Add user to organization 4 | 5 | ```sh 6 | Usage: phylum org member add [OPTIONS] ... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   User(s) to be added 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Add user `demo@veracode.com` to the `sample` organization 32 | $ phylum org -o sample member add demo@veracode.com 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_member_list.md: -------------------------------------------------------------------------------- 1 | # phylum group member list 2 | 3 | List group members 4 | 5 | ```sh 6 | Usage: phylum group member --group list [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce member list in json format (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Examples 27 | 28 | ```sh 29 | # List all group members for the `sample` group 30 | $ phylum group member --group sample list 31 | ``` 32 | -------------------------------------------------------------------------------- /tests/fixtures/lock_generate_csproj/lock_generate_csproj.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_member_add.md: -------------------------------------------------------------------------------- 1 | # phylum group member add 2 | 3 | Add user to group 4 | 5 | ```sh 6 | Usage: phylum group member --group add [OPTIONS] ... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   User(s) to be added 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Add user `demo@veracode.com` to the `sample` group 32 | $ phylum group member --group sample add demo@veracode.com 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_member_remove.md: -------------------------------------------------------------------------------- 1 | # phylum org member remove 2 | 3 | Remove user from organization 4 | 5 | ```sh 6 | Usage: phylum org member remove [OPTIONS] ... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   User(s) to be removed 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Remove user `demo@veracode.com` from the `sample` organization 32 | $ phylum org -o sample member remove demo@veracode.com 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/commands/phylum_project_status.md: -------------------------------------------------------------------------------- 1 | # phylum project status 2 | 3 | Get current project information 4 | 5 | ```sh 6 | Usage: phylum project status [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-p`, `--project` `` 15 |   Specify a project to use for analysis 16 | 17 | `-g`, `--group` `` 18 |   Specify a group to use for analysis 19 | 20 | `-o`, `--org` `` 21 |   Phylum organization 22 | 23 | `-v`, `--verbose`... 24 |   Increase the level of verbosity (the maximum is -vvv) 25 | 26 | `-q`, `--quiet`... 27 |   Reduce the level of verbosity (the maximum is -qq) 28 | 29 | `-h`, `--help` 30 |   Print help 31 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [features] 9 | default = ["full-docs"] 10 | # Enable any CLI features that change the available commands 11 | full-docs = ["phylum-cli/selfmanage", "phylum-cli/extensions"] 12 | 13 | [dependencies] 14 | anyhow = "1.0.53" 15 | clap = { version = "4.0.9", features = ["wrap_help"] } 16 | clap_complete = "4.0.2" 17 | home = "0.5.3" 18 | log = "0.4.14" 19 | simplelog = "0.12.0" 20 | fs_extra = "1.2.0" 21 | phylum-cli = { path = "../cli", default-features = false } 22 | clap_markdown = { path = "../clap_markdown" } 23 | 24 | [dev-dependencies] 25 | tempfile = "3.3.0" 26 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_token.md: -------------------------------------------------------------------------------- 1 | # phylum auth token 2 | 3 | Return the current authentication token 4 | 5 | ```sh 6 | Usage: phylum auth token [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-b`, `--bearer` 12 |   Output the short-lived bearer token for the Phylum API 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Examples 27 | 28 | ```sh 29 | # Output long-lived refresh token 30 | $ phylum auth token 31 | 32 | # Output short-lived bearer token 33 | $ phylum auth token --bearer 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_link.md: -------------------------------------------------------------------------------- 1 | # phylum org link 2 | 3 | Select an organization as default for all operations 4 | 5 | ```sh 6 | Usage: phylum org link [OPTIONS] [ORG] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[ORG]` 12 |   Organization to use as default 13 | 14 | ## Options 15 | 16 | `-v`, `--verbose`... 17 |   Increase the level of verbosity (the maximum is -vvv) 18 | 19 | `-q`, `--quiet`... 20 |   Reduce the level of verbosity (the maximum is -qq) 21 | 22 | `-h`, `--help` 23 |   Print help 24 | 25 | ## Examples 26 | 27 | ```sh 28 | # Interactively select an organization for all future operations 29 | $ phylum org link 30 | 31 | # Set `sample` as the default organization for all future operations 32 | $ phylum org link sample 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_member_remove.md: -------------------------------------------------------------------------------- 1 | # phylum group member remove 2 | 3 | Remove user from group 4 | 5 | ```sh 6 | Usage: phylum group member --group remove [OPTIONS] ... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   User(s) to be removed 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Remove user `demo@veracode.com` from the `sample` group 32 | $ phylum group member --group sample remove demo@veracode.com 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/commands/phylum_uninstall.md: -------------------------------------------------------------------------------- 1 | # phylum uninstall 2 | 3 | Uninstall the Phylum CLI 4 | 5 | ```sh 6 | Usage: phylum uninstall [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-p`, `--purge` 12 |   Remove all files, including configuration files (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Examples 27 | 28 | ```sh 29 | # Remove installed phylum binary and data files 30 | $ phylum uninstall 31 | 32 | # Remove all installed phylum files 33 | $ phylum uninstall --purge 34 | ``` 35 | -------------------------------------------------------------------------------- /phylum_project/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phylum_project" 3 | description = "Phylum project configuration handling" 4 | repository = "https://github.com/phylum-dev/cli" 5 | version = "0.1.0" 6 | authors = ["Veracode, Inc. "] 7 | license = "GPL-3.0-or-later" 8 | edition = "2021" 9 | rust-version = "1.85.0" 10 | 11 | [dependencies] 12 | chrono = { version = "^0.4", default-features = false, features = [ 13 | "serde", 14 | "clock", 15 | ] } 16 | dunce = "1.0.5" 17 | log = "0.4.6" 18 | phylum_types = { git = "https://github.com/phylum-dev/phylum-types", branch = "development" } 19 | serde = { version = "1.0.144", features = ["derive"] } 20 | serde_yaml = "0.9.2" 21 | 22 | [dev-dependencies] 23 | tempfile = "3.4.0" 24 | uuid = "1.3.0" 25 | -------------------------------------------------------------------------------- /doc_templates/phylum_parse.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Details 6 | 7 | The following order is used to determine which dependency file will be parsed: 8 | 9 | - CLI `DEPENDENCY_FILE` argument 10 | - Dependency files in the `.phylum_project` file specified during `phylum init` 11 | - Recursive filesystem search 12 | 13 | If any of these locations provides a dependency file, no further search will be 14 | done. Recursive filesystem search takes common ignore files like `.gitignore` 15 | and `.ignore` into account. 16 | 17 | ## Examples 18 | 19 | ```sh 20 | # Parse a dependency file 21 | $ phylum parse package-lock.json 22 | 23 | # Parse the `Cargo.lock` and `lockfile` files as cargo dependency files 24 | $ phylum parse --type cargo Cargo.lock lockfile 25 | ``` 26 | -------------------------------------------------------------------------------- /docs/commands/phylum_exception_list.md: -------------------------------------------------------------------------------- 1 | # phylum exception list 2 | 3 | List active analysis exceptions 4 | 5 | ```sh 6 | Usage: phylum exception list [OPTIONS] <--group |--project > 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-g`, `--group` `` 15 |   Group to list exceptions for 16 | 17 | `-p`, `--project` `` 18 |   Project to list exceptions for 19 | 20 | `-o`, `--org` `` 21 |   Phylum organization 22 | 23 | `-v`, `--verbose`... 24 |   Increase the level of verbosity (the maximum is -vvv) 25 | 26 | `-q`, `--quiet`... 27 |   Reduce the level of verbosity (the maximum is -qq) 28 | 29 | `-h`, `--help` 30 |   Print help 31 | -------------------------------------------------------------------------------- /docs/commands/phylum_extension_new.md: -------------------------------------------------------------------------------- 1 | # phylum extension new 2 | 3 | Create a new extension 4 | 5 | ```sh 6 | Usage: phylum extension new [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 | 13 | ## Options 14 | 15 | `-o`, `--org` `` 16 |   Phylum organization 17 | 18 | `-v`, `--verbose`... 19 |   Increase the level of verbosity (the maximum is -vvv) 20 | 21 | `-q`, `--quiet`... 22 |   Reduce the level of verbosity (the maximum is -qq) 23 | 24 | `-h`, `--help` 25 |   Print help 26 | 27 | ## Details 28 | 29 | This command will create a "Hello, World!" extension skeleton that can be 30 | modified for a specific use. See the [Extension Quickstart] guide for more 31 | detail. 32 | 33 | [Extension Quickstart]: ../extensions/extension_quickstart.md 34 | -------------------------------------------------------------------------------- /docs/commands/phylum_org_list.md: -------------------------------------------------------------------------------- 1 | # phylum org list 2 | 3 | List all organizations the user is a member of 4 | 5 | ```sh 6 | Usage: phylum org list [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Examples 27 | 28 | ```sh 29 | # List all organizations the user is a member of 30 | $ phylum org list 31 | 32 | # List all organizations the user is a member of with json output 33 | $ phylum org list --json 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/commands/phylum_extension.md: -------------------------------------------------------------------------------- 1 | # phylum extension 2 | 3 | Manage extensions 4 | 5 | ```sh 6 | Usage: phylum extension [OPTIONS] [COMMAND] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum extension install](./phylum_extension_install.md) 26 | * [phylum extension list](./phylum_extension_list.md) 27 | * [phylum extension new](./phylum_extension_new.md) 28 | * [phylum extension run](./phylum_extension_run.md) 29 | * [phylum extension uninstall](./phylum_extension_uninstall.md) 30 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_member.md: -------------------------------------------------------------------------------- 1 | # phylum group member 2 | 3 | Manage group members 4 | 5 | ```sh 6 | Usage: phylum group member [OPTIONS] --group 7 | ``` 8 | 9 | ## Options 10 | 11 | `-g`, `--group` `` 12 |   Group to manage the members for 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Commands 27 | 28 | * [phylum group member add](./phylum_group_member_add.md) 29 | * [phylum group member list](./phylum_group_member_list.md) 30 | * [phylum group member remove](./phylum_group_member_remove.md) 31 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_revoke-token.md: -------------------------------------------------------------------------------- 1 | # phylum auth revoke-token 2 | 3 | Revoke an API token 4 | 5 | ```sh 6 | Usage: phylum auth revoke-token [OPTIONS] [TOKEN_NAME]... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[TOKEN_NAME]` 12 |   Unique token names which identify the tokens 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Interactively select tokens to revoke. 32 | $ phylum auth revoke-token 33 | 34 | # Revoke tokens "token1" and "token2". 35 | $ phylum auth revoke-token "token1" "token2" 36 | ``` 37 | -------------------------------------------------------------------------------- /docs/commands/phylum_project.md: -------------------------------------------------------------------------------- 1 | # phylum project 2 | 3 | Manage Phylum projects 4 | 5 | ```sh 6 | Usage: phylum project [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum project create](./phylum_project_create.md) 26 | * [phylum project delete](./phylum_project_delete.md) 27 | * [phylum project link](./phylum_project_link.md) 28 | * [phylum project list](./phylum_project_list.md) 29 | * [phylum project status](./phylum_project_status.md) 30 | * [phylum project update](./phylum_project_update.md) 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "3" 3 | members = [ 4 | "cli", 5 | "lockfile", 6 | "xtask", 7 | "clap_markdown", 8 | "phylum_project", 9 | "lockfile_generator", 10 | "vulnreach_types", 11 | ] 12 | 13 | [patch.crates-io] 14 | # TODO: Remove this patch when `deno`, and specifically `deno_ffi`, is updated to a newer version that depends on 15 | # versions of `libffi` and `libffi-sys` that are from 08 APR 2025 or later (4.0.0 and 3.2.0, respectively). Until 16 | # then, building on `macos-latest` runners without this patch will fail. 17 | # https://github.com/phylum-dev/cli/issues/1478 18 | # Ref: https://github.com/libffi-rs/libffi-rs/issues/109 19 | # https://github.com/libffi-rs/libffi-rs/pull/110 20 | libffi-sys = { git = 'https://github.com/libffi-rs/libffi-rs', rev = "8df0df577317bdca2c2b5e9ae263ba0e98fa9076" } 21 | -------------------------------------------------------------------------------- /extensions/duplicates/README.md: -------------------------------------------------------------------------------- 1 | # Phylum duplicates extension 2 | 3 | A [Phylum CLI][phylum-cli] extension that checks for duplicates in your 4 | dependency graph. 5 | 6 | [phylum-cli]: https://github.com/phylum-dev/cli 7 | 8 | This extension serves as a simple example and has its source code fully 9 | explained in our [extension example documentation]. 10 | 11 | [extension example documentation]: https://docs.phylum.io/cli/extensions/extension_example 12 | 13 | ## Installation and basic usage 14 | 15 | Clone the repository and install the extension via the Phylum CLI. 16 | 17 | ```console 18 | git clone https://github.com/phylum-dev/cli 19 | phylum extension install cli/extensions/duplicates 20 | ``` 21 | 22 | Check for duplicates by pointing the extension at your lockfile: 23 | 24 | ```console 25 | phylum duplicates ./package-lock.json 26 | ``` 27 | -------------------------------------------------------------------------------- /lockfile_generator/src/dotnet.rs: -------------------------------------------------------------------------------- 1 | //! C# .NET ecosystem. 2 | 3 | use std::path::{Path, PathBuf}; 4 | use std::process::Command; 5 | 6 | use crate::{Error, Generator, Result}; 7 | 8 | pub struct Dotnet; 9 | 10 | impl Generator for Dotnet { 11 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 12 | let project_path = manifest_path 13 | .parent() 14 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 15 | Ok(project_path.join("packages.lock.json")) 16 | } 17 | 18 | fn command(&self, manifest_path: &Path) -> Command { 19 | let mut command = Command::new("dotnet"); 20 | command.arg("restore").arg(manifest_path).arg("--use-lock-file"); 21 | command 22 | } 23 | 24 | fn tool(&self) -> &'static str { 25 | ".NET" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/fixtures/pnpm-lock-v5.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | lodash: 4.17.21 3 | lockfileVersion: 5.1 4 | packages: 5 | /lodash/4.17.21: 6 | dev: false 7 | resolution: 8 | integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 9 | 10 | /use-sync-external-store/1.2.2_react@18.3.1: 11 | resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} 12 | peerDependencies: 13 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 14 | dependencies: 15 | react: 18.3.1 16 | 17 | /@types/eslint__js/8.42.3: 18 | resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==} 19 | dependencies: 20 | '@types/eslint': 9.6.1 21 | dev: true 22 | specifiers: 23 | lodash: ^4.17.21 24 | -------------------------------------------------------------------------------- /docs/commands/phylum_project_update.md: -------------------------------------------------------------------------------- 1 | # phylum project update 2 | 3 | Update a project 4 | 5 | ```sh 6 | Usage: phylum project update [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-i`, `--project-id` `` 12 |   ID of the project to be updated 13 | 14 | `-g`, `--group` `` 15 |   Group that owns the project 16 | 17 | `-n`, `--name` `` 18 |   New project name 19 | 20 | `-r`, `--repository-url` `` 21 |   New repository URL 22 | 23 | `-l`, `--default-label` `` 24 |   Default project label 25 | 26 | `-o`, `--org` `` 27 |   Phylum organization 28 | 29 | `-v`, `--verbose`... 30 |   Increase the level of verbosity (the maximum is -vvv) 31 | 32 | `-q`, `--quiet`... 33 |   Reduce the level of verbosity (the maximum is -qq) 34 | 35 | `-h`, `--help` 36 |   Print help 37 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_list.md: -------------------------------------------------------------------------------- 1 | # phylum group list 2 | 3 | List all groups the user is a member of 4 | 5 | ```sh 6 | Usage: phylum group list [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-o`, `--org` `` 15 |   Phylum organization 16 | 17 | `-v`, `--verbose`... 18 |   Increase the level of verbosity (the maximum is -vvv) 19 | 20 | `-q`, `--quiet`... 21 |   Reduce the level of verbosity (the maximum is -qq) 22 | 23 | `-h`, `--help` 24 |   Print help 25 | 26 | ## Examples 27 | 28 | ```sh 29 | # List all groups the user is a member of 30 | $ phylum group list 31 | 32 | # List all groups the user is a member of with json output 33 | $ phylum group list --json 34 | 35 | # List all groups for the `test` organization 36 | $ phylum group list --org test 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth_set-token.md: -------------------------------------------------------------------------------- 1 | # phylum auth set-token 2 | 3 | Set the current authentication token 4 | 5 | ```sh 6 | Usage: phylum auth set-token [OPTIONS] [TOKEN] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[TOKEN]` 12 |   Authentication token to store (read from stdin if omitted) 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Supply the token directly on the command line 32 | $ phylum auth set-token ph0_UyqKk8yRmuO4gRx52os3obQevBluJTGsepQw0bLRmX0 33 | 34 | # Supply the token on stdin 35 | $ phylum auth set-token 36 | ph0_UyqKk8yRmuO4gRx52os3obQevBluJTGsepQw0bLRmX0 37 | ``` 38 | -------------------------------------------------------------------------------- /extensions/find-permissions/README.md: -------------------------------------------------------------------------------- 1 | # Find extension exceptions 2 | 3 | A [Phylum CLI][phylum-cli] extension that helps with finding required extension 4 | sandboxing exceptions. 5 | 6 | ## Installation and basic usage 7 | 8 | Clone the repository and install the extension via the Phylum CLI. 9 | 10 | ```console 11 | git clone https://github.com/phylum-dev/cli 12 | phylum extension install cli/extensions/find-permissions 13 | ``` 14 | 15 | Run `find-permissions` against a command you want to test: 16 | 17 | ```console 18 | phylum find-permissions --read --write --bin /usr/bin/ls 19 | ``` 20 | 21 | To find out more about the usage of `find-permissions`, check its `--help` or 22 | visit the [extension sandboxing documentation][ext_sandbox_doc]. 23 | 24 | [phylum-cli]: https://github.com/phylum-dev/cli 25 | [ext_sandbox_doc]: https://docs.phylum.io/cli/extensions/extension_sandboxing#finding-required-exceptions 26 | -------------------------------------------------------------------------------- /docs/commands/phylum_project_link.md: -------------------------------------------------------------------------------- 1 | # phylum project link 2 | 3 | Link a repository to a project 4 | 5 | ```sh 6 | Usage: phylum project link [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Name of the project 13 | 14 | ## Options 15 | 16 | `-g`, `--group` `` 17 |   Group owning the project 18 | 19 | `-o`, `--org` `` 20 |   Phylum organization 21 | 22 | `-v`, `--verbose`... 23 |   Increase the level of verbosity (the maximum is -vvv) 24 | 25 | `-q`, `--quiet`... 26 |   Reduce the level of verbosity (the maximum is -qq) 27 | 28 | `-h`, `--help` 29 |   Print help 30 | 31 | ## Examples 32 | 33 | ```sh 34 | # Link current folder to an existing project named `sample` 35 | $ phylum project link sample 36 | 37 | # Link current folder to an existing project named `sample` owned by the group `sGroup` 38 | $ phylum project link -g sGroup sample 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/commands/phylum_extension_run.md: -------------------------------------------------------------------------------- 1 | # phylum extension run 2 | 3 | Run an extension from a directory 4 | 5 | ```sh 6 | Usage: phylum extension run [OPTIONS] [OPTIONS]... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 | 13 | `[OPTIONS]` 14 |   Extension parameters 15 | 16 | ## Options 17 | 18 | `-y`, `--yes` 19 |   Automatically accept requested permissions 20 | 21 | `-o`, `--org` `` 22 |   Phylum organization 23 | 24 | `-v`, `--verbose`... 25 |   Increase the level of verbosity (the maximum is -vvv) 26 | 27 | `-q`, `--quiet`... 28 |   Reduce the level of verbosity (the maximum is -qq) 29 | 30 | ## Details 31 | 32 | The extension will be run without prior installation. 33 | 34 | The first set of options are for the `run` command. The second set of options 35 | are for the extension. 36 | 37 | ## Examples 38 | 39 | ```sh 40 | phylum extension run --yes ./my-extension --help 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/commands/phylum_auth.md: -------------------------------------------------------------------------------- 1 | # phylum auth 2 | 3 | Manage authentication, registration, and API keys 4 | 5 | ```sh 6 | Usage: phylum auth [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-o`, `--org` `` 12 |   Phylum organization 13 | 14 | `-v`, `--verbose`... 15 |   Increase the level of verbosity (the maximum is -vvv) 16 | 17 | `-q`, `--quiet`... 18 |   Reduce the level of verbosity (the maximum is -qq) 19 | 20 | `-h`, `--help` 21 |   Print help 22 | 23 | ## Commands 24 | 25 | * [phylum auth create-token](./phylum_auth_create-token.md) 26 | * [phylum auth list-tokens](./phylum_auth_list-tokens.md) 27 | * [phylum auth login](./phylum_auth_login.md) 28 | * [phylum auth register](./phylum_auth_register.md) 29 | * [phylum auth revoke-token](./phylum_auth_revoke-token.md) 30 | * [phylum auth set-token](./phylum_auth_set-token.md) 31 | * [phylum auth status](./phylum_auth_status.md) 32 | * [phylum auth token](./phylum_auth_token.md) 33 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_create.md: -------------------------------------------------------------------------------- 1 | # phylum group create 2 | 3 | Create a new group 4 | 5 | ```sh 6 | Usage: phylum group create [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Name for the new group 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Create a new group named `sample` 32 | $ phylum group create sample 33 | 34 | # Create a group `sample` under the `test` organization 35 | $ phylum group create --org test sample 36 | 37 | # Make `test` the default organization for all operations, 38 | # then create a new group `sample` under it. 39 | $ phylum org link test 40 | $ phylum group create sample 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/commands/phylum_group_delete.md: -------------------------------------------------------------------------------- 1 | # phylum group delete 2 | 3 | Delete a group 4 | 5 | ```sh 6 | Usage: phylum group delete [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Name for the group to be deleted 13 | 14 | ## Options 15 | 16 | `-o`, `--org` `` 17 |   Phylum organization 18 | 19 | `-v`, `--verbose`... 20 |   Increase the level of verbosity (the maximum is -vvv) 21 | 22 | `-q`, `--quiet`... 23 |   Reduce the level of verbosity (the maximum is -qq) 24 | 25 | `-h`, `--help` 26 |   Print help 27 | 28 | ## Examples 29 | 30 | ```sh 31 | # Delete an existing group named `sample` 32 | $ phylum group delete sample 33 | 34 | # Delete the group `sample` from the `test` organization 35 | $ phylum group delete --org test sample 36 | 37 | # Make `test` the default organization for all operations, 38 | # then delete the group `sample` from it. 39 | $ phylum org link test 40 | $ phylum group delete sample 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/commands/phylum_project_create.md: -------------------------------------------------------------------------------- 1 | # phylum project create 2 | 3 | Create a new project 4 | 5 | ```sh 6 | Usage: phylum project create [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Name of the project 13 | 14 | ## Options 15 | 16 | `-g`, `--group` `` 17 |   Group which will be the owner of the project 18 | 19 | `-r`, `--repository-url` `` 20 |   Repository URL of the project 21 | 22 | `-o`, `--org` `` 23 |   Phylum organization 24 | 25 | `-v`, `--verbose`... 26 |   Increase the level of verbosity (the maximum is -vvv) 27 | 28 | `-q`, `--quiet`... 29 |   Reduce the level of verbosity (the maximum is -qq) 30 | 31 | `-h`, `--help` 32 |   Print help 33 | 34 | ## Examples 35 | 36 | ```sh 37 | # Create a new project named `sample` 38 | $ phylum project create sample 39 | 40 | # Create a new project named `sample` owned by the group `sGroup` 41 | $ phylum project create -g sGroup sample 42 | ``` 43 | -------------------------------------------------------------------------------- /lockfile/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "phylum_lockfile" 3 | description = "Package lockfile parsers used by Phylum" 4 | version = "0.1.0" 5 | authors = ["Veracode, Inc. "] 6 | edition = "2021" 7 | rust-version = "1.85.0" 8 | 9 | [features] 10 | default = [] 11 | generator = ["lockfile_generator"] 12 | 13 | [dependencies] 14 | anyhow = "1.0.44" 15 | ignore = "0.4.20" 16 | lockfile_generator = { path = "../lockfile_generator", optional = true } 17 | log = "0.4.6" 18 | nom = "8.0.0" 19 | nom-language = "0.1.0" 20 | phylum_types = { git = "https://github.com/phylum-dev/phylum-types", branch = "development" } 21 | purl = "0.1.1" 22 | quick-xml = { version = "0.38.3", features = [ 23 | "encoding", 24 | "overlapped-lists", 25 | "serialize", 26 | ] } 27 | serde = { version = "1.0.144", features = ["derive"] } 28 | serde_json = "1.0.85" 29 | serde_yaml = "0.9.2" 30 | thiserror = "2.0.3" 31 | toml = "0.9.5" 32 | urlencoding = "2.1.2" 33 | walkdir = "2.3.2" 34 | 35 | [dev-dependencies] 36 | tempfile = "3.6.0" 37 | -------------------------------------------------------------------------------- /lockfile_generator/src/go.rs: -------------------------------------------------------------------------------- 1 | //! Go ecosystem. 2 | 3 | use std::ffi::OsStr; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | use crate::{Error, Generator, Result}; 8 | 9 | pub struct Go; 10 | 11 | impl Generator for Go { 12 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 13 | let project_path = manifest_path 14 | .parent() 15 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 16 | Ok(project_path.join("go.sum")) 17 | } 18 | 19 | fn command(&self, _manifest_path: &Path) -> Command { 20 | let mut command = Command::new("go"); 21 | command.args(["get", "-d"]); 22 | command 23 | } 24 | 25 | fn tool(&self) -> &'static str { 26 | "Go" 27 | } 28 | 29 | fn check_prerequisites(&self, manifest_path: &Path) -> Result<()> { 30 | if manifest_path.file_name() != Some(OsStr::new("go.mod")) { 31 | Err(Error::InvalidManifest(manifest_path.to_path_buf())) 32 | } else { 33 | Ok(()) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/commands/phylum_project_list.md: -------------------------------------------------------------------------------- 1 | # phylum project list 2 | 3 | List all existing projects 4 | 5 | ```sh 6 | Usage: phylum project list [OPTIONS] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-j`, `--json` 12 |   Produce output in json format (default: false) 13 | 14 | `-g`, `--group` `` 15 |   Group to list projects for 16 | 17 | `--no-group` 18 |   Exclude all group projects from the output 19 | 20 | `-o`, `--org` `` 21 |   Phylum organization 22 | 23 | `-v`, `--verbose`... 24 |   Increase the level of verbosity (the maximum is -vvv) 25 | 26 | `-q`, `--quiet`... 27 |   Reduce the level of verbosity (the maximum is -qq) 28 | 29 | `-h`, `--help` 30 |   Print help 31 | 32 | ## Examples 33 | 34 | ```sh 35 | # List all existing projects 36 | $ phylum project list 37 | 38 | # List all existing projects with json output 39 | $ phylum project list --json 40 | 41 | # List all existing projects for the `sample` group 42 | $ phylum project list -g sample 43 | 44 | # List all existing projects for the `demo` organization 45 | $ phylum project list --organization demo 46 | ``` 47 | -------------------------------------------------------------------------------- /lockfile_generator/src/bundler.rs: -------------------------------------------------------------------------------- 1 | //! Ruby bundler ecosystem. 2 | 3 | use std::ffi::OsStr; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | use crate::{Error, Generator, Result}; 8 | 9 | pub struct Bundler; 10 | 11 | impl Generator for Bundler { 12 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 13 | let project_path = manifest_path 14 | .parent() 15 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 16 | Ok(project_path.join("Gemfile.lock")) 17 | } 18 | 19 | fn command(&self, _manifest_path: &Path) -> Command { 20 | let mut command = Command::new("bundle"); 21 | command.args(["lock"]); 22 | command 23 | } 24 | 25 | fn tool(&self) -> &'static str { 26 | "Bundler" 27 | } 28 | 29 | fn check_prerequisites(&self, manifest_path: &Path) -> Result<()> { 30 | if manifest_path.file_name() != Some(OsStr::new("Gemfile")) { 31 | Err(Error::InvalidManifest(manifest_path.to_path_buf())) 32 | } else { 33 | Ok(()) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lockfile_generator/src/pipenv.rs: -------------------------------------------------------------------------------- 1 | //! Python pipenv ecosystem. 2 | 3 | use std::ffi::OsStr; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | use crate::{Error, Generator, Result}; 8 | 9 | pub struct Pipenv; 10 | 11 | impl Generator for Pipenv { 12 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 13 | let project_path = manifest_path 14 | .parent() 15 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 16 | Ok(project_path.join("Pipfile.lock")) 17 | } 18 | 19 | fn command(&self, _manifest_path: &Path) -> Command { 20 | let mut command = Command::new("pipenv"); 21 | command.args(["lock"]); 22 | command 23 | } 24 | 25 | fn tool(&self) -> &'static str { 26 | "Pipenv" 27 | } 28 | 29 | fn check_prerequisites(&self, manifest_path: &Path) -> Result<()> { 30 | if manifest_path.file_name() != Some(OsStr::new("Pipfile")) { 31 | Err(Error::InvalidManifest(manifest_path.to_path_buf())) 32 | } else { 33 | Ok(()) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lockfile_generator/src/poetry.rs: -------------------------------------------------------------------------------- 1 | //! Python poetry ecosystem. 2 | 3 | use std::ffi::OsStr; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | use crate::{Error, Generator, Result}; 8 | 9 | pub struct Poetry; 10 | 11 | impl Generator for Poetry { 12 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 13 | let project_path = manifest_path 14 | .parent() 15 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 16 | Ok(project_path.join("poetry.lock")) 17 | } 18 | 19 | fn command(&self, _manifest_path: &Path) -> Command { 20 | let mut command = Command::new("poetry"); 21 | command.args(["lock"]); 22 | command 23 | } 24 | 25 | fn tool(&self) -> &'static str { 26 | "Poetry" 27 | } 28 | 29 | fn check_prerequisites(&self, manifest_path: &Path) -> Result<()> { 30 | if manifest_path.file_name() != Some(OsStr::new("pyproject.toml")) { 31 | Err(Error::InvalidManifest(manifest_path.to_path_buf())) 32 | } else { 33 | Ok(()) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cli/tests/config.rs: -------------------------------------------------------------------------------- 1 | use phylum_cli::config::{AuthInfo, Config, ConnectionInfo}; 2 | use phylum_types::types::auth::RefreshToken; 3 | 4 | use crate::common::{TestCli, API_URL}; 5 | 6 | #[test] 7 | fn pass_api_key_through_env() { 8 | const ENV_TOKEN: &str = "ENV VARIABLE TOKEN"; 9 | 10 | TestCli::builder() 11 | .cwd_temp() 12 | .build() 13 | .cmd() 14 | .env("PHYLUM_API_KEY", ENV_TOKEN) 15 | .args(["auth", "token"]) 16 | .assert() 17 | .success() 18 | .stdout(format!("{ENV_TOKEN}\n")); 19 | } 20 | 21 | #[test] 22 | fn ignore_empty_token() { 23 | const CONFIG_TOKEN: &str = "CONFIGTOKEN"; 24 | 25 | let mut config = Config::default(); 26 | config.connection = ConnectionInfo { uri: API_URL.into() }; 27 | config.auth_info = AuthInfo::new(Some(RefreshToken::new(CONFIG_TOKEN))); 28 | 29 | TestCli::builder() 30 | .with_config(config) 31 | .cwd_temp() 32 | .build() 33 | .cmd() 34 | .env("PHYLUM_API_KEY", "") 35 | .args(["auth", "token"]) 36 | .assert() 37 | .success() 38 | .stdout(format!("{CONFIG_TOKEN}\n")); 39 | } 40 | -------------------------------------------------------------------------------- /docs/commands/phylum_extension_install.md: -------------------------------------------------------------------------------- 1 | # phylum extension install 2 | 3 | Install extension 4 | 5 | ```sh 6 | Usage: phylum extension install [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 | 13 | ## Options 14 | 15 | `-y`, `--yes` 16 |   Accept permissions and overwrite existing extensions (same as \`--overwrite --accept-permissions\`) 17 | 18 | `--accept-permissions` 19 |   Automatically accept requested permissions 20 | 21 | `--overwrite` 22 |   Overwrite existing extension 23 | 24 | `-o`, `--org` `` 25 |   Phylum organization 26 | 27 | `-v`, `--verbose`... 28 |   Increase the level of verbosity (the maximum is -vvv) 29 | 30 | `-q`, `--quiet`... 31 |   Reduce the level of verbosity (the maximum is -qq) 32 | 33 | `-h`, `--help` 34 |   Print help 35 | 36 | ## Details 37 | 38 | The extension will be installed under `$XDG_DATA_HOME/phylum/extensions/`. 39 | If `$XDG_DATA_HOME` is not set, it will default to `$HOME/.local/share/phylum/extensions/`. 40 | 41 | Once installed, the extension will be accessible via the Phylum CLI: 42 | 43 | ```sh 44 | phylum [OPTIONS]... 45 | ``` 46 | -------------------------------------------------------------------------------- /tests/fixtures/nested_bom.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://cyclonedx.org/schema/bom-1.5.schema.json", 3 | "bomFormat": "CycloneDX", 4 | "specVersion": "1.5", 5 | "components": [ 6 | { 7 | "type": "framework", 8 | "name": "FrameworkA", 9 | "version": "1.0", 10 | "scope": "required", 11 | "purl": "pkg:npm/FrameworkA@1.0", 12 | "components": [ 13 | { 14 | "type": "library", 15 | "name": "LibA", 16 | "version": "1.1", 17 | "scope": "required", 18 | "purl": "pkg:npm/LibA@1.1" 19 | }, 20 | { 21 | "type": "library", 22 | "name": "LibB", 23 | "version": "1.2", 24 | "purl": "pkg:pypi/LibB@1.2" 25 | } 26 | ] 27 | }, 28 | { 29 | "type": "application", 30 | "name": "AppA", 31 | "version": "1.0", 32 | "scope": "required", 33 | "purl": "pkg:pypi/AppA@1.0" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # This is the configuration file for GitHub's Dependabot, used here for version updates. 2 | # See the documentation for all configuration options: 3 | # https://docs.github.com/code-security/dependabot 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | --- 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" 9 | directory: "/" 10 | schedule: 11 | interval: weekly 12 | # Only worry about major version updates here. Other updates are accounted for in 13 | # a separate workflow in an effort to reduce the number of PRs opened by Dependabot. 14 | ignore: 15 | - dependency-name: "*" 16 | update-types: ["version-update:semver-minor", "version-update:semver-patch"] 17 | 18 | # All untrusted GitHub actions should be pinned to an explicit SHA instead of a tag name. 19 | # Each pin should include a comment about the version of the action to which it corresponds. 20 | # Dependabot will update these comments at the same time that it updates the pin. 21 | - package-ecosystem: "github-actions" 22 | directory: "/" 23 | schedule: 24 | interval: weekly 25 | -------------------------------------------------------------------------------- /extensions/duplicates/main.ts: -------------------------------------------------------------------------------- 1 | import { mapValues } from "https://deno.land/std@0.150.0/collections/map_values.ts"; 2 | import { distinct } from "https://deno.land/std@0.150.0/collections/distinct.ts"; 3 | import { groupBy } from "https://deno.land/std@0.150.0/collections/group_by.ts"; 4 | 5 | // Ensure lockfile argument is present. 6 | if (Deno.args.length != 1) { 7 | console.error("Usage: phylum duplicates "); 8 | Deno.exit(1); 9 | } 10 | 11 | // Parse lockfile using Phylum's API. 12 | const lockfile = await Phylum.parseDependencyFile(Deno.args[0]); 13 | 14 | // Group all versions for the same dependency together. 15 | const groupedDeps = groupBy(lockfile.packages, (dep) => dep.name); 16 | 17 | // Reduce each dependency to a list of its versions. 18 | const reducedDeps = mapValues( 19 | groupedDeps, 20 | (deps) => deps!.map((dep) => dep.version), 21 | ); 22 | 23 | for (const [dep, versions] of Object.entries(reducedDeps)) { 24 | // Deduplicate identical versions. 25 | const distinctVersions = distinct(versions); 26 | 27 | // Print all dependencies with more than one version. 28 | if (distinctVersions.length > 1) { 29 | console.log(`${dep}:`, distinctVersions); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/fixtures/go.mod: -------------------------------------------------------------------------------- 1 | module cli/example 2 | 3 | go 1.22.2 4 | 5 | require ( 6 | github.com/go-chi/chi/v5 v5.0.12 7 | github.com/rs/zerolog v1.32.0 8 | 9 | example.com/othermodule v1.2.3 10 | example.com/othermodule v1.2.4 11 | example.com/othermodule v1.2.5 12 | example.com/replacedmodule v1.2.3 13 | 14 | example.com/thismodule v1.2.3 15 | example.com/thismodule v1.2.4 16 | 17 | example.com/excludedmodule v1.2.3 18 | example.com/excludedmodule v1.2.4 19 | 20 | ) 21 | 22 | require ( 23 | github.com/mattn/go-colorable v0.1.13 // indirect 24 | github.com/mattn/go-isatty v0.0.20 // indirect 25 | golang.org/x/sys v0.12.0 // indirect 26 | example.com/othermodule v1.2.3 // indirect 27 | ) 28 | 29 | replace example.com/replacedmodule => ../replacedmodule 30 | 31 | replace example.com/othermodule v1.2.3 => example.com/newmodule v3.2.1 32 | 33 | replace ( 34 | example.com/othermodule v1.2.4 => example.com/newmodule v3.2.2 35 | example.com/othermodule v1.2.5 => example.com/newmodule v3.2.3 36 | ) 37 | 38 | exclude example.com/thismodule v1.2.3 39 | 40 | exclude example.com/thismodule v1.2.4 41 | 42 | exclude ( 43 | example.com/excludedmodule v1.2.3 44 | example.com/excludedmodule v1.2.4 45 | ) 46 | -------------------------------------------------------------------------------- /cli/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | // This build script exists as a workaround for the following issue: 4 | // https://github.com/rust-lang/rust/issues/34283 5 | // 6 | // This issue is present under Windows in debug builds. 7 | // 8 | // The CLI has a long `match` statement in the body of a function. LLVM makes it 9 | // so that the stack space required by the `match` statement is proportional to 10 | // the sum of the stack space requirements for each branch, rather than to the 11 | // maximum of all of the branches (which is what happens on higher optimization 12 | // levels and on different targets). 13 | // 14 | // As a result, Windows debug builds will result in a stack overflow when run, 15 | // because of too high a stack utilization. We can prevent this by expanding the 16 | // available stack space at link time. 17 | // 18 | // Since a larger stack space has no advantage for us other than preventing this 19 | // issue, we apply the fix only to the affected platforms. 20 | 21 | fn main() { 22 | let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); 23 | let profile = env::var("PROFILE").unwrap(); 24 | 25 | if os == "windows" && profile == "debug" { 26 | println!("cargo:rustc-link-arg=/STACK:0x800000"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lockfile_generator/src/maven.rs: -------------------------------------------------------------------------------- 1 | //! Java maven ecosystem. 2 | 3 | use std::path::{Path, PathBuf}; 4 | use std::process::Command; 5 | 6 | use crate::{Error, Generator, Result}; 7 | 8 | pub struct Maven; 9 | 10 | #[cfg(not(windows))] 11 | fn maven_command() -> Command { 12 | Command::new("mvn") 13 | } 14 | 15 | #[cfg(windows)] 16 | fn maven_command() -> Command { 17 | // Maven uses a batch script on Windows 18 | Command::new("mvn.cmd") 19 | } 20 | 21 | impl Generator for Maven { 22 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 23 | let project_path = manifest_path 24 | .parent() 25 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 26 | Ok(project_path.join("effective-pom.xml")) 27 | } 28 | 29 | fn command(&self, manifest_path: &Path) -> Command { 30 | let lockfile_path = self.lockfile_path(manifest_path).unwrap(); 31 | let mut command = maven_command(); 32 | command.args([ 33 | "-q", 34 | "help:effective-pom", 35 | &format!("-Doutput={}", lockfile_path.display()), 36 | ]); 37 | command 38 | } 39 | 40 | fn tool(&self) -> &'static str { 41 | "Maven" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/commands/phylum_history.md: -------------------------------------------------------------------------------- 1 | # phylum history 2 | 3 | Return information about historical jobs 4 | 5 | ```sh 6 | Usage: phylum history [OPTIONS] [JOB_ID] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[JOB_ID]` 12 |   The job id to query 13 | 14 | ## Options 15 | 16 | `-j`, `--json` 17 |   Produce output in json format (default: false) 18 | 19 | `-p`, `--project` `` 20 |   Project to be queried 21 | 22 | `-g`, `--group` `` 23 |   Group to be queried 24 | 25 | `-o`, `--org` `` 26 |   Phylum organization 27 | 28 | `-v`, `--verbose`... 29 |   Increase the level of verbosity (the maximum is -vvv) 30 | 31 | `-q`, `--quiet`... 32 |   Reduce the level of verbosity (the maximum is -qq) 33 | 34 | `-h`, `--help` 35 |   Print help 36 | 37 | ## Examples 38 | 39 | ```sh 40 | # List the last 30 analysis runs 41 | $ phylum history 42 | 43 | # View the analysis results of a historical job 44 | $ phylum history 338ea79f-0e82-4422-9769-4e583a84599f 45 | 46 | # View a list of analysis runs for the `sample` project 47 | $ phylum history --project sample 48 | 49 | # Show analysis runs for the `sample` project of the `demo` group under the `test` org 50 | $ phylum history --org test --group demo --project sample 51 | ``` 52 | -------------------------------------------------------------------------------- /docs/commands/phylum_exception_add.md: -------------------------------------------------------------------------------- 1 | # phylum exception add 2 | 3 | Add a new analysis exception 4 | 5 | ```sh 6 | Usage: phylum exception add [OPTIONS] <--group |--project > 7 | ``` 8 | 9 | ## Options 10 | 11 | `-g`, `--group` `` 12 |   Group to add exception to 13 | 14 | `-p`, `--project` `` 15 |   Project to add exceptions to 16 | 17 | `--package-type` `` 18 |   Package type of the package to add an exception for 19 |   Accepted values: `npm`, `gem`, `pypi`, `maven`, `nuget`, `golang`, `cargo` 20 | 21 | `-n`, `--name` `` 22 |   Fully qualified name of the package to add an exception for 23 | 24 | `--version` `` 25 |   Version of the package to add an exception for 26 | 27 | `--purl` `` 28 |   Package in PURL format 29 | 30 | `-r`, `--reason` `` 31 |   Reason for adding this exception 32 | 33 | `-s`, `--no-suggestions` 34 |   Do not query package firewall to make suggestions 35 | 36 | `-o`, `--org` `` 37 |   Phylum organization 38 | 39 | `-v`, `--verbose`... 40 |   Increase the level of verbosity (the maximum is -vvv) 41 | 42 | `-q`, `--quiet`... 43 |   Reduce the level of verbosity (the maximum is -qq) 44 | 45 | `-h`, `--help` 46 |   Print help 47 | -------------------------------------------------------------------------------- /xtask/fixtures/test-project/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "js-tokens@^3.0.0 || ^4.0.0": 6 | version "4.0.0" 7 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 8 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== 9 | 10 | loose-envify@^1.1.0: 11 | version "1.4.0" 12 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 13 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== 14 | dependencies: 15 | js-tokens "^3.0.0 || ^4.0.0" 16 | 17 | object-assign@^4.1.1: 18 | version "4.1.1" 19 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 20 | integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 21 | 22 | react@^17.0.2: 23 | version "17.0.2" 24 | resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" 25 | integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== 26 | dependencies: 27 | loose-envify "^1.1.0" 28 | object-assign "^4.1.1" 29 | -------------------------------------------------------------------------------- /xtask/src/gencomp.rs: -------------------------------------------------------------------------------- 1 | //! Generate shell completion files. 2 | 3 | use std::path::Path; 4 | 5 | use anyhow::Result; 6 | use clap_complete::generate_to; 7 | use clap_complete::shells::{Bash, Fish, Zsh}; 8 | use log::info; 9 | 10 | use crate::{cli_app, project_root}; 11 | 12 | /// Generate shell completion files. 13 | pub fn gencomp() -> Result<()> { 14 | let comp_root = project_root().join("target").join("completions"); 15 | let _ = std::fs::remove_dir_all(&comp_root); 16 | 17 | copy_completions(&comp_root)?; 18 | 19 | Ok(()) 20 | } 21 | 22 | fn copy_completions(completions_path: &Path) -> Result<()> { 23 | let mut app = cli_app(); 24 | 25 | std::fs::create_dir_all(completions_path)?; 26 | 27 | info!(" Generating Bash completions"); 28 | generate_to(Bash, &mut app, "phylum", completions_path)?; 29 | info!(" Generating Zsh completions"); 30 | generate_to(Zsh, &mut app, "phylum", completions_path)?; 31 | info!(" Generating Fish completions"); 32 | generate_to(Fish, &mut app, "phylum", completions_path)?; 33 | 34 | Ok(()) 35 | } 36 | 37 | #[cfg(test)] 38 | mod test { 39 | use tempfile::tempdir; 40 | 41 | use super::*; 42 | 43 | #[test] 44 | fn generate_completions() { 45 | let tmp_dir = tempdir().unwrap(); 46 | 47 | copy_completions(tmp_dir.path()).unwrap(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/commands/phylum_exception_remove.md: -------------------------------------------------------------------------------- 1 | # phylum exception remove 2 | 3 | Remove an existing analysis exception 4 | 5 | ```sh 6 | Usage: phylum exception remove [OPTIONS] <--group |--project > 7 | ``` 8 | 9 | ## Options 10 | 11 | `-g`, `--group` `` 12 |   Group to remove exception from 13 | 14 | `-p`, `--project` `` 15 |   Project to remove exceptions from 16 | 17 | `--package-type` `` 18 |   Package type of the exception which should be removed 19 |   Accepted values: `npm`, `gem`, `pypi`, `maven`, `nuget`, `golang`, `cargo` 20 | 21 | `-n`, `--name` `` 22 |   Fully qualified package name of the exception which should be removed 23 | 24 | `--version` `` 25 |   Package version of the exception which should be removed 26 | 27 | `--purl` `` 28 |   Package in PURL format 29 | 30 | `--id` `` 31 |   Issue ID of the exception which should be removed 32 | 33 | `--tag` `` 34 |   Issue tag of the exception which should be removed 35 | 36 | `-o`, `--org` `` 37 |   Phylum organization 38 | 39 | `-v`, `--verbose`... 40 |   Increase the level of verbosity (the maximum is -vvv) 41 | 42 | `-q`, `--quiet`... 43 |   Reduce the level of verbosity (the maximum is -qq) 44 | 45 | `-h`, `--help` 46 |   Print help 47 | -------------------------------------------------------------------------------- /tests/fixtures/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | pyinstaller = "*" 8 | nose = "*" 9 | unittest2 = {version = ">=1.0,<3.0", markers="python_version < '2.7.9' or (python_version >= '3.0' and python_version < '3.4')"} 10 | 11 | [packages] 12 | requests = { version = "==2.24.0", extras = ["socks"] } 13 | click = "*" 14 | selenium = "*" 15 | chromedriver-py = "==91.0.4472.19" 16 | furl = "*" 17 | twilio = "*" 18 | discord-webhook = "*" 19 | pycryptodome = "==3.9.8" 20 | questionary = "*" 21 | spinlog = "*" 22 | slackclient = "*" 23 | playsound = "*" 24 | prompt_toolkit = "*" 25 | aiohttp = "*" 26 | pyobjc = { version = "*", sys_platform = "== 'darwin'" } 27 | async-timeout = "*" 28 | amazoncaptcha = "*" 29 | browser-cookie3 = "*" 30 | coloredlogs = "*" 31 | apprise = "*" 32 | price-parser = "*" 33 | pypresence = "==4.0.0" 34 | pywin32 = { version = "*", sys_platform = "== 'win32'" } 35 | psutil = "*" 36 | stdiomask = "*" 37 | packaging = "*" 38 | config = "*" 39 | lxml = "*" 40 | dnspython = "*" 41 | records = ">0.5.0" 42 | django = { git = "https://github.com/django/django.git", ref = "1.11.4", editable = true } 43 | "e682b37" = {file = "https://github.com/divio/django-cms/archive/release/3.4.x.zip"} 44 | "e1839a8" = {path = ".", editable = true} 45 | pywinusb = { version = "*", os_name = "=='nt'", index="pypi"} 46 | 47 | [requires] 48 | python_version = "3.8" -------------------------------------------------------------------------------- /doc_templates/phylum_analyze.md: -------------------------------------------------------------------------------- 1 | {PH-HEADER} 2 | 3 | {PH-MARKDOWN} 4 | 5 | ## Details 6 | 7 | The following order is used to determine which dependency file will be analyzed: 8 | 9 | - CLI `DEPENDENCY_FILE` argument 10 | - Dependency files in the `.phylum_project` file specified during `phylum init` 11 | - Recursive filesystem search 12 | 13 | If any of these locations provides a dependency file, no further search will be 14 | done. Recursive filesystem search takes common ignore files like `.gitignore` 15 | and `.ignore` into account. 16 | 17 | ## Examples 18 | 19 | ```sh 20 | # Analyze your project's default dependency files 21 | $ phylum analyze 22 | 23 | # Analyze a Maven lockfile with a verbose json response 24 | $ phylum analyze --json --verbose effective-pom.xml 25 | 26 | # Analyze a PyPI dependency file and apply a label 27 | $ phylum analyze --label test_branch requirements.txt 28 | 29 | # Analyze a Poetry lockfile and return the results to the `sample` project 30 | $ phylum analyze -p sample poetry.lock 31 | 32 | # Analyze a NuGet lockfile using the `sample` project and `sGroup` group 33 | $ phylum analyze -p sample -g sGroup packages.lock.json 34 | 35 | # Analyze a RubyGems lockfile and return a verbose response with only critical malware 36 | $ phylum analyze --verbose --filter=crit,mal Gemfile.lock 37 | 38 | # Analyze the `Cargo.lock` and `lockfile` files as cargo dependency files 39 | $ phylum analyze --type cargo Cargo.lock lockfile 40 | ``` 41 | -------------------------------------------------------------------------------- /cli/src/dirs.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::{Path, PathBuf}; 3 | 4 | use anyhow::{anyhow, Result}; 5 | 6 | /// Resolve XDG data directory. 7 | pub fn data_dir() -> Result { 8 | xdg_dir("XDG_DATA_HOME", ".local/share") 9 | } 10 | 11 | /// Resolve XDG config directory. 12 | pub fn config_dir() -> Result { 13 | xdg_dir("XDG_CONFIG_HOME", ".config") 14 | } 15 | 16 | /// Resolve XDG state directory. 17 | pub fn state_dir() -> Result { 18 | xdg_dir("XDG_STATE_HOME", ".local/state") 19 | } 20 | 21 | /// XDG binary directory. 22 | pub fn bin_dir() -> Result { 23 | Ok(home_dir()?.join(".local/bin")) 24 | } 25 | 26 | /// User home directory. 27 | pub fn home_dir() -> Result { 28 | home::home_dir().ok_or_else(|| anyhow!("Couldn't find the user's home directory")) 29 | } 30 | 31 | /// Resolve an XDG directory. 32 | pub fn xdg_dir(env_var: &str, path_suffix: impl AsRef) -> Result { 33 | env::var_os(env_var) 34 | .filter(|s| !s.is_empty()) 35 | .map(|var| Ok(PathBuf::from(var))) 36 | .unwrap_or_else(|| Ok(home_dir()?.join(path_suffix))) 37 | } 38 | 39 | /// Expand leading tildes to the user's home path. 40 | pub fn expand_home_path(path: &str, home: &Path) -> PathBuf { 41 | path.strip_prefix('~') 42 | .filter(|path| path.is_empty() || path.starts_with('/')) 43 | .map(|suffix| home.join(suffix.strip_prefix('/').unwrap_or(suffix))) 44 | .unwrap_or_else(|| PathBuf::from(path)) 45 | } 46 | -------------------------------------------------------------------------------- /tests/fixtures/requirements-unlocked.txt: -------------------------------------------------------------------------------- 1 | nose 2 | nose-cov 3 | beautifulsoup4 4 | keyring >= 4.1.1 # Minimum version 4.1.1 5 | coverage != 3.5 # Version Exclusion. Anything except version 3.5 6 | Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.* 7 | -r other-requirements.txt 8 | ./downloads/numpy-1.9.2-cp34-none-win32.whl 9 | http://wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev1820+49a8884-cp34-none-win_amd64.whl 10 | rejected 11 | green 12 | SomeProject1 13 | SomeProject3 >=1.*,!=2.0 14 | SomeProject4[foo, bar] 15 | SomeProject5~=1.4.2 16 | SomeProject7; sys_platform == 'win32' 17 | SomeProject8 == 1.* 18 | SomeProject10 >= 1.2 --global-option="--no-user-cfg" \ 19 | --install-option="--prefix='/usr/local'" \ 20 | --install-option="--no-compile" 21 | SomeProject11 == 1.2 --hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 \ 22 | --hash=sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7 23 | requests != 2.12.3,>2.12.1 24 | requests [security,tests] >= 2.8.1, != 2.8.2 ; python_version < "2.7" 25 | FooProject1 >1.0 26 | FooProject2 < 1.2 27 | FooProject3 <= 1.3 28 | FooProject4 >= 1.4 29 | FooProject6 != 1.6 30 | FooProject7 ~= 1.7 31 | FooProject8 > 1.8.* 32 | FooProject9 > 2.0.*, !=2.1 33 | FooProject10==1.2.3 --random-flag 34 | git+https://github.com/matiascodesal/git-for-pip-example.git@v1.0.0#egg=my-git-package 35 | localversion >= 2.3.4+1.0.99.local 36 | -------------------------------------------------------------------------------- /docs/alternate_install.md: -------------------------------------------------------------------------------- 1 | # Alternate Installation Methods 2 | 3 | ## Build from source 4 | 5 | Phylum is written in Rust, so you'll need a recent Rust installation to build it (we tested with v1.61.0). See [how to install Rust](https://www.rust-lang.org/tools/install) for more information. 6 | 7 | 1. Clone repository 8 | 9 | ```sh 10 | git clone https://github.com/phylum-dev/cli 11 | ``` 12 | 13 | 2. Build the project 14 | 15 | ```sh 16 | cargo build 17 | ``` 18 | 19 | 3. You can use the executable directly as `./target/debug/phylum` or install it like so: 20 | 21 | ```sh 22 | cargo install --locked --path cli 23 | ``` 24 | 25 | ## Install with Python 26 | 27 | The [`phylum` Python package](https://pypi.org/project/phylum/) provides a script entry point, `phylum-init`, for bootstrapping the Phylum CLI. 28 | 29 | See the [phylum-ci](https://github.com/phylum-dev/phylum-ci) project for full detail. 30 | 31 | ```sh 32 | pipx install phylum 33 | phylum-init 34 | ``` 35 | 36 | ## Install from GitHub release 37 | 38 | Pre-built binaries for select targets are available in our [GitHub releases](https://github.com/phylum-dev/cli/releases). 39 | 40 | Archives can be manually verified by their signature file (which has the `.signature` extension) with `openssl` and the [public key for Phylum](https://raw.githubusercontent.com/phylum-dev/cli/main/scripts/signing-key.pub): 41 | 42 | ```sh 43 | $ openssl dgst -sha256 -verify signing-key.pub -signature phylum-*.zip.signature phylum-*.zip 44 | Verified OK 45 | ``` 46 | -------------------------------------------------------------------------------- /tests/fixtures/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git@github.com:ruby/yaml.git 3 | revision: b89ff5a79c2abbf81612ffe9a6c184db347365c9 4 | tag: v0.2.1 5 | specs: 6 | yaml (0.2.1) 7 | 8 | GIT 9 | remote: https://gist.github.com/cd-work/bb850193cd4d1eff0d7021c9a3899882.git 10 | revision: 24b38dc61f9e2ee241e1f5eba4fdba4b5ed1e737 11 | specs: 12 | main (0.0.0) 13 | 14 | GIT 15 | remote: https://github.com/ruby/benchmark.git 16 | revision: 303ac8f28b9aad6abe95c86bc64ea891f77ac93e 17 | ref: 303ac8f28b9aad6abe95c86bc64ea891f77ac93e 18 | specs: 19 | benchmark (0.2.1) 20 | 21 | PATH 22 | remote: /tmp/csv 23 | specs: 24 | csv (3.2.7) 25 | 26 | GEM 27 | remote: http://rubygems.org/ 28 | specs: 29 | diff-lcs (1.5.0) 30 | rspec (3.11.0) 31 | rspec-core (~> 3.11.0) 32 | rspec-expectations (~> 3.11.0) 33 | rspec-mocks (~> 3.11.0) 34 | rspec-core (3.11.0) 35 | rspec-support (~> 3.11.0) 36 | rspec-expectations (3.11.1) 37 | diff-lcs (>= 1.2.0, < 2.0) 38 | rspec-support 39 | rspec-mocks (3.11.2) 40 | diff-lcs (>= 1.2.0, < 2.0, != 1.2.3) 41 | rspec-support (~> 3.11.0) 42 | rspec-support (3.11.1) 43 | 44 | PLATFORMS 45 | x86_64-linux 46 | 47 | GEM 48 | remote: https://rubygems.org/ 49 | specs: 50 | wirble (0.1.3) 51 | ffi (1.17.0) 52 | ffi (1.17.0-x86_64-linux-gnu) 53 | fake (1.2.3-x86_64-linux-gnu) 54 | 55 | DEPENDENCIES 56 | benchmark! 57 | csv! 58 | main! 59 | rspec (~> 3.11.0)! 60 | wirble 61 | yaml! 62 | 63 | BUNDLED WITH 64 | 2.3.25 65 | -------------------------------------------------------------------------------- /docs/extensions/extension_api.md: -------------------------------------------------------------------------------- 1 | # Extension API 2 | 3 | ## Overview 4 | 5 | Since CLI extensions are built on top of the Deno runtime, they have access to 6 | two different APIs; the Deno API and the Phylum API. 7 | 8 | ## Deno API 9 | 10 | Deno's API is built into the Deno runtime, providing access to all external 11 | interfaces like Network, Disk, or the terminal console. All available 12 | functionality is documented in [Deno's API docs]. This functionality is 13 | available to all extensions without any imports. 14 | 15 | Additionally, Deno also provides a complementary standard library. This includes 16 | utility functions for several commonly used structures like collections, http, 17 | and async. These modules can be imported using the URLs documented in Deno's 18 | standard library documentation or by downloading them and including individual 19 | modules as files. All standard library functionality is documented in [Deno's 20 | standard library docs]. 21 | 22 | [Deno's API docs]: https://doc.deno.land/deno/stable 23 | [Deno's standard library docs]: https://deno.land/std 24 | 25 | The following API methods are not supported: 26 | 27 | - 28 | 29 | ## Phylum API 30 | 31 | The Phylum extension API is documented in the [TypeScript module file]. 32 | 33 | You can keep track of the latest changes by following the [API's changelog]. 34 | 35 | [TypeScript module file]: https://github.com/phylum-dev/cli/blob/main/extensions/phylum.d.ts 36 | [API's changelog]: https://github.com/phylum-dev/cli/blob/main/extensions/CHANGELOG.md 37 | -------------------------------------------------------------------------------- /docs/commands/phylum.md: -------------------------------------------------------------------------------- 1 | # phylum 2 | 3 | Client interface to the Phylum system 4 | 5 | ```sh 6 | Usage: phylum [OPTIONS] [COMMAND] 7 | ``` 8 | 9 | ## Options 10 | 11 | `-c`, `--config` `` 12 |   Sets a custom config file 13 | 14 | `--no-config` 15 |   Ignore all configuration files 16 | 17 | `-t`, `--timeout` `` 18 |   Set the timeout (in seconds) for requests to the Phylum api 19 | 20 | `--ignore-certs` 21 |   Don't validate the server certificate when performing api requests 22 | 23 | `-o`, `--org` `` 24 |   Phylum organization 25 | 26 | `-v`, `--verbose`... 27 |   Increase the level of verbosity (the maximum is -vvv) 28 | 29 | `-q`, `--quiet`... 30 |   Reduce the level of verbosity (the maximum is -qq) 31 | 32 | `-h`, `--help` 33 |   Print help 34 | 35 | `-V`, `--version` 36 |   Print version 37 | 38 | ## Commands 39 | 40 | * [phylum analyze](./phylum_analyze.md) 41 | * [phylum auth](./phylum_auth.md) 42 | * [phylum exception](./phylum_exception.md) 43 | * [phylum extension](./phylum_extension.md) 44 | * [phylum firewall](./phylum_firewall.md) 45 | * [phylum group](./phylum_group.md) 46 | * [phylum history](./phylum_history.md) 47 | * [phylum init](./phylum_init.md) 48 | * [phylum org](./phylum_org.md) 49 | * [phylum package](./phylum_package.md) 50 | * [phylum parse](./phylum_parse.md) 51 | * [phylum ping](./phylum_ping.md) 52 | * [phylum project](./phylum_project.md) 53 | * [phylum status](./phylum_status.md) 54 | * [phylum uninstall](./phylum_uninstall.md) 55 | * [phylum update](./phylum_update.md) 56 | * [phylum version](./phylum_version.md) 57 | -------------------------------------------------------------------------------- /docs/commands/phylum_init.md: -------------------------------------------------------------------------------- 1 | # phylum init 2 | 3 | Setup a new Phylum project 4 | 5 | ```sh 6 | Usage: phylum init [OPTIONS] [PROJECT_NAME] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[PROJECT_NAME]` 12 |   Phylum project name 13 | 14 | ## Options 15 | 16 | `-g`, `--group` `` 17 |   Group which will be the owner of the project 18 | 19 | `-d`, `--dependency-file` `` 20 |   Project-relative dependency file path 21 | 22 | `-t`, `--type` `` 23 |   Dependency file type used for all lockfiles (default: auto) 24 |   Accepted values: `npm`, `yarn`, `pnpm`, `gem`, `pip`, `poetry`, `pipenv`, `mvn`, `gradle`, `msbuild`, `nugetlock`, `nugetconfig`, `gomod`, `go`, `cargo`, `spdx`, `cyclonedx`, `auto` 25 | 26 | `-f`, `--force` 27 |   Overwrite existing configurations without confirmation 28 | 29 | `-r`, `--repository-url` `` 30 |   Repository URL of the project 31 | 32 | `-o`, `--org` `` 33 |   Phylum organization 34 | 35 | `-v`, `--verbose`... 36 |   Increase the level of verbosity (the maximum is -vvv) 37 | 38 | `-q`, `--quiet`... 39 |   Reduce the level of verbosity (the maximum is -qq) 40 | 41 | `-h`, `--help` 42 |   Print help 43 | 44 | ## Examples 45 | 46 | ```sh 47 | # Interactively initialize the Phylum project. 48 | $ phylum init 49 | 50 | # Create the `demo` project with a yarn lockfile and no associated group. 51 | $ phylum init --dependency-file yarn.lock --type yarn demo 52 | 53 | # Create the `demo` project in the `sample` group of the `test` organization. 54 | $ phylum init --org test --group sample demo 55 | ``` 56 | -------------------------------------------------------------------------------- /lockfile/src/parsers/gradle_dep.rs: -------------------------------------------------------------------------------- 1 | use nom::branch::alt; 2 | use nom::bytes::complete::{tag, take_till}; 3 | use nom::combinator::eof; 4 | use nom::Parser; 5 | use nom_language::error::VerboseError; 6 | use phylum_types::types::package::PackageType; 7 | 8 | use crate::parsers::IResult; 9 | use crate::{Package, PackageVersion}; 10 | 11 | pub fn parse(input: &str) -> IResult<&str, Vec> { 12 | let mut pkgs = Vec::new(); 13 | for line in input.lines().filter(filter_line) { 14 | pkgs.push(package(line)?); 15 | } 16 | Ok((input, pkgs)) 17 | } 18 | 19 | // Filter out comments and non-package lines. 20 | fn filter_line(line: &&str) -> bool { 21 | !line.starts_with('#') && !line.starts_with("empty=") && !line.trim().is_empty() 22 | } 23 | 24 | // Take all non-white characters until encountering whitespace or `until`. 25 | fn not_space_until(input: &str, until: char) -> IResult<&str, &str> { 26 | take_till(|c: char| c == until || c.is_whitespace())(input) 27 | } 28 | 29 | fn package(input: &str) -> Result>> { 30 | let (input, group_id) = not_space_until(input, ':')?; 31 | let (input, _) = tag(":")(input)?; 32 | 33 | let (input, artifact_id) = not_space_until(input, ':')?; 34 | let (input, _) = tag(":")(input)?; 35 | 36 | let (input, version) = not_space_until(input, '=')?; 37 | let _ = alt((tag("="), eof)).parse(input)?; 38 | 39 | Ok(Package { 40 | name: format!("{group_id}:{artifact_id}"), 41 | version: PackageVersion::FirstParty(version.to_string()), 42 | package_type: PackageType::Maven, 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /docs/extensions/extension_quickstart.md: -------------------------------------------------------------------------------- 1 | # Extension Quickstart 2 | 3 | ## Creating an extension 4 | 5 | The best way to get started with writing your own Phylum CLI extension, is to 6 | generate an extension skeleton using the `phylum extension new` subcommand. 7 | We'll use `my-extension` as an example in this guide: 8 | 9 | ```sh 10 | phylum extension new my-extension 11 | ``` 12 | 13 | Once finished, we should find a new directory called `my-extension` in our 14 | current working directory, containing the files `main.ts` and `PhylumExt.toml`. 15 | 16 | ## Extension structure 17 | 18 | Extensions always contain at least two files, the manifest describing the 19 | extension (`PhylumExt.toml`), and the entrypoint where the extension's execution 20 | will begin. Any additional source files can be included in the extension 21 | directory and imported from the entrypoint. 22 | 23 | The manifest file contains metadata about the extension beyond its executable 24 | source code. All available options can be found in [the manifest format]. 25 | 26 | [the manifest format]: ./extension_manifest.md 27 | 28 | ## Installation 29 | 30 | Since the generated extension skeleton is a fully functional extension, we can 31 | go ahead and install it right away: 32 | 33 | ```sh 34 | phylum extension install ./my-extension 35 | ``` 36 | 37 | ## Execution 38 | 39 | Once successfully installed, our extension can be executed by using its name as 40 | a subcommand for the phylum CLI: 41 | 42 | ```shellsession 43 | $ phylum my-extension 44 | Hello, World! 45 | ``` 46 | 47 | The `Hello, World!` message confirms that our extension is working correctly. 48 | -------------------------------------------------------------------------------- /xtask/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::{Path, PathBuf}; 3 | 4 | use anyhow::Result; 5 | use clap::Command; 6 | use log::LevelFilter; 7 | use phylum_cli::app; 8 | use simplelog::{ColorChoice, TermLogger, TerminalMode}; 9 | 10 | mod gencomp; 11 | mod gendocs; 12 | 13 | fn main() -> Result<()> { 14 | TermLogger::init( 15 | LevelFilter::Info, 16 | Default::default(), 17 | TerminalMode::Mixed, 18 | ColorChoice::Auto, 19 | )?; 20 | match std::env::args().nth(1).as_deref() { 21 | Some("gendocs") => gendocs::gendocs(), 22 | Some("gencomp") => gencomp::gencomp(), 23 | None | Some("help") => { 24 | print_help(); 25 | Ok(()) 26 | }, 27 | _ => { 28 | print_help(); 29 | Ok(()) 30 | }, 31 | } 32 | } 33 | 34 | /// Print command usage. 35 | fn print_help() { 36 | println!( 37 | r#" 38 | Usage 39 | 40 | cargo run -p xtask 41 | 42 | Available tasks: 43 | 44 | gendocs ....... Generate CLI documentation files 45 | gencomp ....... Generate completion files 46 | "# 47 | ); 48 | } 49 | 50 | /// Return the repository root directory. 51 | fn project_root() -> PathBuf { 52 | Path::new(&env!("CARGO_MANIFEST_DIR")).ancestors().nth(1).unwrap().to_path_buf() 53 | } 54 | 55 | /// Get the CLI app without extensions. 56 | fn cli_app() -> Command { 57 | // Set `XDG_DATA_HOME` to a bogus directory so regardless of installed 58 | // extensions, none of them are ever documented. 59 | env::set_var("XDG_DATA_HOME", "/i/n/v/a/l/i/d"); 60 | 61 | app::app() 62 | } 63 | -------------------------------------------------------------------------------- /lockfile/src/parsers/mod.rs: -------------------------------------------------------------------------------- 1 | use nom::branch::alt; 2 | use nom::bytes::complete::{tag, take_until}; 3 | use nom::character::complete::{line_ending, not_line_ending, space0}; 4 | use nom::combinator::{eof, opt, recognize, rest}; 5 | use nom::error::context; 6 | use nom::multi::many_till; 7 | use nom::sequence::{delimited, terminated}; 8 | use nom::{AsChar, Parser}; 9 | use nom_language::error::VerboseError; 10 | 11 | pub mod gem; 12 | pub mod go_mod; 13 | pub mod go_sum; 14 | pub mod gradle_dep; 15 | pub mod pypi; 16 | pub mod spdx; 17 | pub mod yarn; 18 | 19 | /// Consume everything until the next `\n` or `\r\n`. 20 | fn take_till_line_end(input: &str) -> IResult<&str, &str> { 21 | recognize(terminated(not_line_ending, line_ending)).parse(input) 22 | } 23 | 24 | /// Consume everything until the next `\n\n` or `\r\n\r\n`. 25 | fn take_till_blank_line(input: &str) -> IResult<&str, &str> { 26 | recognize(alt((take_until("\n\n"), take_until("\r\n\r\n")))).parse(input) 27 | } 28 | 29 | /// Consume the next line. 30 | /// 31 | /// This supports both `\n` and `\r\n`. It also skips line continuations 32 | /// (`\\\n`, `\\\r\n`) and stops on EOF. 33 | fn take_continued_line(mut input: &str) -> IResult<&str, ()> { 34 | loop { 35 | // Get everything up to the next NL or EOF. 36 | let (new_input, line) = recognize(alt((take_till_line_end, rest))).parse(input)?; 37 | input = new_input; 38 | 39 | // Stop consuming lines once there are no continuations. 40 | if !line.ends_with("\\\n") && !line.ends_with("\\\r\n") { 41 | break; 42 | } 43 | } 44 | 45 | Ok((input, ())) 46 | } 47 | 48 | type IResult = nom::IResult>; 49 | -------------------------------------------------------------------------------- /docs/commands/phylum_package.md: -------------------------------------------------------------------------------- 1 | # phylum package 2 | 3 | Retrieve the details of a specific package 4 | 5 | ```sh 6 | Usage: phylum package [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Package ecosystem type 13 |   Accepted values: `npm`, `rubygems`, `pypi`, `maven`, `nuget`, `golang`, `cargo` 14 | 15 | `` 16 |   The name of the package. 17 | 18 | `` 19 |   The version of the package. 20 | 21 | ## Options 22 | 23 | `-j`, `--json` 24 |   Produce output in json format (default: false) 25 | 26 | `-f`, `--filter` `` 27 |   Provide a filter used to limit the issues displayed 28 | 29 |     EXAMPLES: 30 |     \# Show only issues with severity of at least 'high' 31 |         --filter=high 32 | 33 |     \# Show issues with severity of 'critical' in the 'author' 34 |     and 'engineering' domains 35 |         --filter=crit,aut,eng 36 | 37 | `-o`, `--org` `` 38 |   Phylum organization 39 | 40 | `-v`, `--verbose`... 41 |   Increase the level of verbosity (the maximum is -vvv) 42 | 43 | `-q`, `--quiet`... 44 |   Reduce the level of verbosity (the maximum is -qq) 45 | 46 | `-h`, `--help` 47 |   Print help 48 | 49 | ## Details 50 | 51 | If the requested package has not yet been analyzed by Phylum, it will 52 | automatically be submitted for [processing]. 53 | 54 | [processing]: ../../knowledge_base/faq.md 55 | 56 | ## Examples 57 | 58 | ```sh 59 | # Query specific package details 60 | $ phylum package -t npm axios 0.19.0 61 | ``` 62 | -------------------------------------------------------------------------------- /bump_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # To release a new version of the CLI: 4 | # 5 | # * Run this script on a branch to bump the version 6 | # * Submit a PR for the version bump and, after approval, merge it to the default branch 7 | # * Run tag.sh from the default branch 8 | 9 | set -eu 10 | 11 | LATEST=$(git describe --tags --abbrev=0 --exclude="*-rc*") 12 | printf "Latest release: %s\n\n" "${LATEST}" 13 | printf "Git log since latest release:\n" 14 | git log --oneline HEAD "^${LATEST}" 15 | printf "\n" 16 | 17 | printf "version (w/o a leading 'v'): " 18 | read -r version 19 | TAG=v${version} 20 | 21 | today=$(date '+%Y-%m-%d') 22 | printf "\nUpdating CHANGELOG, bumping version, running 'cargo check', and adding files for commit ...\n\n" 23 | sed -i'.bak' "s/\(## Unreleased\)/\1\n\n## ${version} - ${today}/" CHANGELOG.md 24 | rm -f CHANGELOG.md.bak 25 | 26 | sed -E -i'.bak' "s/^version = \"([^\"]*)\"/version = \"${version}\"/" cli/Cargo.toml 27 | rm -f cli/Cargo.toml.bak 28 | cargo check 29 | git add Cargo.lock 30 | git add CHANGELOG.md 31 | git add cli/Cargo.toml 32 | 33 | printf "\nUpdating extension changelog...\n" 34 | sed -i'.bak' "s/\(## Unreleased\)/\1\n\n## ${version} - ${today}/" extensions/CHANGELOG.md 35 | rm extensions/CHANGELOG.md.bak 36 | git add extensions/CHANGELOG.md 37 | 38 | commit_message="Bump to ${TAG}" 39 | printf "\nFiles to be added and committed with message: \"%s\"\n\n" "${commit_message}" 40 | git status 41 | 42 | printf "Press enter to proceed with the commit..." 43 | read -r _ 44 | 45 | git commit -F - < CommandResult { 16 | let pretty_print = !matches.get_flag("json"); 17 | let status = PhylumStatus::new(matches); 18 | status.write_stdout(pretty_print); 19 | Ok(ExitCode::Ok) 20 | } 21 | 22 | #[derive(Serialize, Default)] 23 | pub struct PhylumStatus { 24 | pub dependency_files: Vec, 25 | pub project: Option, 26 | pub group: Option, 27 | pub root: Option, 28 | 29 | // JSON-only fields: 30 | created_at: Option>, 31 | id: Option, 32 | } 33 | 34 | impl PhylumStatus { 35 | fn new(matches: &ArgMatches) -> Self { 36 | let mut status = PhylumStatus::default(); 37 | let project = phylum_project::get_current_project(); 38 | 39 | // Add dependency files. 40 | status.dependency_files = config::depfiles(matches, project.as_ref()).unwrap_or_default(); 41 | 42 | // Populate project details. 43 | if let Some(project) = project { 44 | status.created_at = Some(project.created_at); 45 | status.root = Some(project.root().clone()); 46 | status.project = Some(project.name); 47 | status.group = project.group_name; 48 | status.id = Some(project.id); 49 | } 50 | 51 | status 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docs/extensions/extension_rest_api.md: -------------------------------------------------------------------------------- 1 | # Direct Phylum API Requests 2 | 3 | ## Phylum REST API 4 | 5 | Phylum provides a versioned REST API for retrieving all available data. This 6 | REST API can be used directly by extensions if there is no TypeScript API 7 | available. 8 | 9 | All endpoints are documented here: 10 | 11 | 12 | ## Extension API requests 13 | 14 | To make a Request to Phylum's REST API, you can use the built-in `Phylum.fetch` 15 | function, which takes care of authentication and finding the correct base URI. 16 | The following example retrieves projects owned by the user which do not belong 17 | to any group: 18 | 19 | ```ts 20 | // Create a fetch request to the `/data/projects/overview` endpoint. 21 | const reply = await Phylum.fetch( 22 | Phylum.ApiVersion.V0, 23 | '/data/projects/overview', 24 | ); 25 | 26 | // Parse the reply as JSON. 27 | const projects = await reply.json(); 28 | 29 | // Output all our projects. 30 | console.log(projects); 31 | ``` 32 | 33 | The last parameter matches [Deno's `fetch` function] and can be overwritten to 34 | send more complicated requests. The following example creates a new Phylum 35 | project through the API: 36 | 37 | [Deno's `fetch` function]: https://deno.land/api@latest?s=fetch 38 | 39 | ```ts 40 | // Create a fetch request to the `/data/projects` endpoint. 41 | const reply = await Phylum.fetch( 42 | Phylum.ApiVersion.V0, 43 | '/data/projects', 44 | { 45 | method: 'POST', 46 | body: JSON.stringify({ 47 | name: 'api_example', 48 | }), 49 | }, 50 | ); 51 | 52 | // Parse the reply as JSON. 53 | const project = await reply.json(); 54 | 55 | // Output the new project. 56 | console.log(project); 57 | ``` 58 | -------------------------------------------------------------------------------- /lockfile_generator/src/cargo.rs: -------------------------------------------------------------------------------- 1 | //! Rust cargo ecosystem. 2 | 3 | use std::path::{Path, PathBuf}; 4 | use std::process::Command; 5 | 6 | use serde::Deserialize; 7 | 8 | use crate::{Error, Generator, Result}; 9 | 10 | pub struct Cargo; 11 | 12 | impl Generator for Cargo { 13 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 14 | let manifest_arg = format!("--manifest-path={}", manifest_path.display()); 15 | let output = Command::new("cargo") 16 | .args(["locate-project", &manifest_arg, "--workspace"]) 17 | .output()?; 18 | 19 | // Ensure command was successful. 20 | if !output.status.success() { 21 | return Err(Error::NonZeroExit(output)); 22 | } 23 | 24 | // Parse metadata output. 25 | let stdout = String::from_utf8(output.stdout).map_err(Error::InvalidUtf8)?; 26 | let project_location: ProjectLocation = serde_json::from_str(&stdout)?; 27 | 28 | // Go from root manifest to root project. 29 | let workspace_root = match project_location.root.parent() { 30 | Some(workspace_root) => workspace_root, 31 | None => return Err(Error::InvalidManifest(project_location.root.clone())), 32 | }; 33 | 34 | Ok(workspace_root.join("Cargo.lock")) 35 | } 36 | 37 | fn command(&self, manifest_path: &Path) -> Command { 38 | let mut command = Command::new("cargo"); 39 | command.arg("generate-lockfile").arg("--manifest-path").arg(manifest_path); 40 | command 41 | } 42 | 43 | fn tool(&self) -> &'static str { 44 | "Cargo" 45 | } 46 | } 47 | 48 | /// Output of `cargo locate-project`. 49 | #[derive(Deserialize)] 50 | struct ProjectLocation { 51 | root: PathBuf, 52 | } 53 | -------------------------------------------------------------------------------- /docs/commands/phylum_parse.md: -------------------------------------------------------------------------------- 1 | # phylum parse 2 | 3 | Parse dependency files and output their packages as JSON 4 | 5 | ```sh 6 | Usage: phylum parse [OPTIONS] [DEPENDENCY_FILE]... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[DEPENDENCY_FILE]` 12 |   Path to the dependency file to parse 13 | 14 | ## Options 15 | 16 | `-t`, `--type` `` 17 |   Dependency file type used for all lockfiles (default: auto) 18 |   Accepted values: `npm`, `yarn`, `pnpm`, `gem`, `pip`, `poetry`, `pipenv`, `mvn`, `gradle`, `msbuild`, `nugetlock`, `nugetconfig`, `gomod`, `go`, `cargo`, `spdx`, `cyclonedx`, `auto` 19 | 20 | `--skip-sandbox` 21 |   Run lockfile generation without sandbox protection 22 | 23 | `--no-generation` 24 |   Disable generation of lockfiles from manifests 25 | 26 | `-o`, `--org` `` 27 |   Phylum organization 28 | 29 | `-v`, `--verbose`... 30 |   Increase the level of verbosity (the maximum is -vvv) 31 | 32 | `-q`, `--quiet`... 33 |   Reduce the level of verbosity (the maximum is -qq) 34 | 35 | `-h`, `--help` 36 |   Print help 37 | 38 | ## Details 39 | 40 | The following order is used to determine which dependency file will be parsed: 41 | 42 | - CLI `DEPENDENCY_FILE` argument 43 | - Dependency files in the `.phylum_project` file specified during `phylum init` 44 | - Recursive filesystem search 45 | 46 | If any of these locations provides a dependency file, no further search will be 47 | done. Recursive filesystem search takes common ignore files like `.gitignore` 48 | and `.ignore` into account. 49 | 50 | ## Examples 51 | 52 | ```sh 53 | # Parse a dependency file 54 | $ phylum parse package-lock.json 55 | 56 | # Parse the `Cargo.lock` and `lockfile` files as cargo dependency files 57 | $ phylum parse --type cargo Cargo.lock lockfile 58 | ``` 59 | -------------------------------------------------------------------------------- /tests/fixtures/requirements-locked.txt: -------------------------------------------------------------------------------- 1 | # The full `pip` requirements file format is documented here: 2 | # https://pip.pypa.io/en/stable/reference/requirements-file-format/ 3 | 4 | # This is a comment. 5 | 6 | # The next two packages are examples of output from using `pip-tools` to run a command like: 7 | # pip-compile requirements/requirements.in 8 | alembic==1.10.3 9 | # via -r requirements/requirements.in 10 | amqp==5.0.9 --hash=sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0 \ 11 | --hash=sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef 12 | # via kombu 13 | 14 | # Whitespace is allowed in requirement specifiers 15 | attrs == 20.2.0 # This is an inline comment 16 | 17 | # The requirement specifier does not have to be at the beginning of the line 18 | flask==2.2.2 19 | 20 | requests[security,tests]==2.28.1 21 | 22 | werkzeug==2.9.2 ; python_version >= "3.7" and python_version < "3.12" 23 | 24 | localversion==2.3.4+1.0.99.local 25 | 26 | attr @ file:///tmp/attr 27 | 28 | numpy @ file:///tmp/testing/numpy-1.23.5-pp38-pypy38_pp73-win_amd64.whl 29 | 30 | git-for-pip-example @ \ 31 | git+https://github.com/matiascodesal/git-for-pip-example.git@v1.0.0 32 | 33 | tomli @ https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl 34 | 35 | -e git+ssh://git@github.com/phylum-dev/phylum-ci.git@7d6d859ad368d1ab0a933f24679e3d3c08a40eac#egg=phylum 36 | 37 | -e /tmp/editable ; python_version >= "3.7" and python_version < "3.12" 38 | 39 | --index-url https://unused.veracode.com/simple/ 40 | --index-url=https://mirror1.veracode.com/simple/ 41 | other-registry-a==3.2.1 42 | -ihttps://mirror2.veracode.com/simple/ 43 | --extra-index-url=https://mirror3.veracode.com/simple/ 44 | other-registry==1.2.3 45 | -------------------------------------------------------------------------------- /docs/commands/phylum_firewall_log.md: -------------------------------------------------------------------------------- 1 | # phylum firewall log 2 | 3 | Show firewall activity log 4 | 5 | ```sh 6 | Usage: phylum firewall log [OPTIONS] 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `` 12 |   Firewall group to list log activity for 13 | 14 | ## Options 15 | 16 | `-j`, `--json` 17 |   Produce output in json format (default: false) 18 | 19 | `--package-type` `` 20 |   Only show logs matching this package type 21 |   Accepted values: `npm`, `gem`, `pypi`, `maven`, `nuget`, `golang`, `cargo` 22 | 23 | `--purl` `` 24 |   Only show logs matching this PURL 25 | 26 | `--action` `` 27 |   Only show logs matching this log action 28 |   Accepted values: `Download`, `AnalysisSuccess`, `AnalysisFailure`, `AnalysisWarning` 29 | 30 | `--before` `` 31 |   Only show logs created before this timestamp (RFC3339 format) 32 | 33 | `--after` `` 34 |   Only show logs created after this timestamp (RFC3339 format) 35 | 36 | `--limit` `` 37 |   Maximum number of log entries to show 38 | 39 | `-o`, `--org` `` 40 |   Phylum organization 41 | 42 | `-v`, `--verbose`... 43 |   Increase the level of verbosity (the maximum is -vvv) 44 | 45 | `-q`, `--quiet`... 46 |   Reduce the level of verbosity (the maximum is -qq) 47 | 48 | `-h`, `--help` 49 |   Print help 50 | 51 | ## Examples 52 | 53 | ```sh 54 | # Show logs for packages which failed analysis for the group `demo`. 55 | $ phylum firewall log demo --action AnalysisFailure 56 | 57 | # Show logs which were created after 2024 for the group `demo`. 58 | $ phylum firewall log demo --after 2024-01-01T00:00:0.0Z 59 | 60 | # Show logs for libc regardless of its version for the group `demo`. 61 | $ phylum firewall log demo --package pkg:cargo/libc 62 | ``` 63 | -------------------------------------------------------------------------------- /lockfile_generator/src/yarn.rs: -------------------------------------------------------------------------------- 1 | //! JavaScript yarn ecosystem. 2 | 3 | use std::ffi::OsStr; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | 7 | use crate::{npm, Error, Generator, Result}; 8 | 9 | pub struct Yarn; 10 | 11 | impl Generator for Yarn { 12 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 13 | let workspace_root = npm::find_workspace_root(manifest_path)?; 14 | Ok(workspace_root.join("yarn.lock")) 15 | } 16 | 17 | fn command(&self, _manifest_path: &Path) -> Command { 18 | let mut command = Command::new("yarn"); 19 | command.args(["install", "--mode=skip-build", "--mode=update-lockfile"]); 20 | command 21 | } 22 | 23 | fn tool(&self) -> &'static str { 24 | "Yarn" 25 | } 26 | 27 | fn check_prerequisites(&self, manifest_path: &Path) -> Result<()> { 28 | if manifest_path.file_name() != Some(OsStr::new("package.json")) { 29 | return Err(Error::InvalidManifest(manifest_path.to_path_buf())); 30 | } 31 | 32 | let yarn_version = yarn_version(manifest_path)?; 33 | if yarn_version.starts_with("1.") { 34 | let version = yarn_version.trim().into(); 35 | return Err(Error::UnsupportedCommandVersion("yarn", "2.0.0+", version)); 36 | } 37 | 38 | Ok(()) 39 | } 40 | } 41 | 42 | /// Get the yarn version of the project. 43 | fn yarn_version(manifest_path: &Path) -> Result { 44 | let canonicalized = dunce::canonicalize(manifest_path)?; 45 | let project_path = canonicalized 46 | .parent() 47 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 48 | 49 | let output = Command::new("yarn").arg("--version").current_dir(project_path).output()?; 50 | if output.status.success() { 51 | Ok(String::from_utf8_lossy(&output.stdout).into()) 52 | } else { 53 | Err(Error::NonZeroExit(output)) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/tests/parse.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use predicates::prelude::*; 4 | use tempfile::TempDir; 5 | 6 | use crate::common::TestCli; 7 | 8 | #[test] 9 | fn parse_with_project_lockfile() { 10 | // Setup CLI with temp dir. 11 | let test_cli = TestCli::builder().cwd_temp().build(); 12 | let temp_path = test_cli.temp_path(); 13 | 14 | // Write .phylum_project to temp dir. 15 | let config = "id: 00000000-0000-0000-0000-000000000000\nname: test\ncreated_at: \ 16 | 2000-01-01T00:00:00.0Z\nlockfile_path: ./package-lock.json\nlockfile_type: npm"; 17 | fs::write(temp_path.join(".phylum_project"), config).unwrap(); 18 | 19 | // Copy lockfile to temp dir. 20 | fs::copy("../tests/fixtures/package-lock.json", temp_path.join("package-lock.json")).unwrap(); 21 | 22 | test_cli 23 | .cmd() 24 | .args(["parse"]) 25 | .assert() 26 | .success() 27 | .stdout(predicate::str::contains("\"name\": \"typescript\"")); 28 | } 29 | 30 | #[test] 31 | fn parse_with_project_lockfile_relative_paths() { 32 | // Setup CLI with temp dir. 33 | let tempdir = TempDir::new().unwrap(); 34 | let sensitive_dir = tempdir.path().join("sensitive_dir_name"); 35 | fs::create_dir_all(&sensitive_dir).unwrap(); 36 | let test_cli = TestCli::builder().cwd(sensitive_dir.clone()).build(); 37 | 38 | // Write .phylum_project to temp dir. 39 | let config = "id: 00000000-0000-0000-0000-000000000000\nname: test\ncreated_at: \ 40 | 2000-01-01T00:00:00.0Z\nlockfile_path: ./package-lock.json\nlockfile_type: npm"; 41 | fs::write(sensitive_dir.join(".phylum_project"), config).unwrap(); 42 | 43 | // Copy lockfile to temp dir. 44 | fs::copy("../tests/fixtures/package-lock.json", sensitive_dir.join("./package-lock.json")) 45 | .unwrap(); 46 | 47 | let not_sensitive_dir = predicate::str::contains("sensitive_dir_name").not(); 48 | test_cli.cmd().args(["parse"]).assert().success().stdout(not_sensitive_dir); 49 | } 50 | -------------------------------------------------------------------------------- /docs/analyzing_dependencies.md: -------------------------------------------------------------------------------- 1 | # Analyzing Dependencies 2 | 3 | After setting up a Phylum [project](../knowledge_base/create_project.md), you can begin analysis by running: 4 | 5 | ```sh 6 | phylum analyze 7 | ``` 8 | 9 | The default response will provide an overall summary result to indicate whether the [project's established policy](../knowledge_base/policy.md) has been met. If there are still packages being processed, an incomplete status will be indicated. Any policy violations will be reported, along with a link to the complete report. 10 | 11 | ```shellsession 12 | ❯ phylum analyze 13 | ✅ Successfully parsed dependency file "requirements.txt" as type "pip" 14 | ✅ Successfully parsed dependency file "package-lock.json" as type "npm" 15 | ✅ Job ID: 3accba15-b0dc-43d2-b8ce-f5700360e3bd 16 | 17 | Phylum Supply Chain Risk Analysis — FAILURE 18 | 19 | [npm] cacheable-request@6.1.0 20 | [VLN] cacheable-request@6.1.0 is vulnerable to Regular Expression Denial of Service 21 | [npm] ci-info@3.8.0 22 | [AUT] Author of ci-info@3.8.0 is using a disposable email domain 23 | [npm] trim@0.0.1 24 | [VLN] trim@0.0.1 is vulnerable to Regular Expression Denial of Service 25 | [pypi] crpytography@0.1 26 | [MAL] crpytography@0.1 may be a typosquatted package 27 | [MAL] crpytography@0.1 is vulnerable to a dependency confusion attack. 28 | [pypi] cryptography@38.0.4 29 | [VLN] cryptography@38.0.4 is vulnerable to Vulnerable OpenSSL included 30 | [pypi] ghostscript@0.7 31 | [LIC] Commercial license risk detected in ghostscript@0.7 32 | [pypi] pyyaml@5.3.1 33 | [VLN] PyYAML@5.3.1 is vulnerable to Improper Input Validation 34 | 35 | You can find the interactive report here: 36 | https://app.phylum.io/projects/e5eab4d2-d27d-42ac-bbad-f3ff5c588f54?label=uncategorized 37 | ``` 38 | 39 | If you prefer JSON formatted output, you can leverage the `--json` flag. 40 | 41 | ```sh 42 | phylum analyze --json > output.json 43 | ``` 44 | 45 | If the analysis fails the project's policy, the command's exit code will be set to `100`. 46 | -------------------------------------------------------------------------------- /.github/workflows/cargo-update.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Update Deps 3 | 4 | on: 5 | workflow_dispatch: 6 | # Run every Monday at 0530 UTC 7 | schedule: 8 | - cron: '30 5 * * 1' 9 | 10 | jobs: 11 | cargo-update: 12 | name: Update dependencies 13 | runs-on: ubuntu-latest 14 | env: 15 | UPDATE_BRANCH_NAME: auto-cargo-update 16 | steps: 17 | - name: Checkout the repo 18 | uses: actions/checkout@v6 19 | 20 | - name: Cargo update 21 | run: cargo update 22 | 23 | - name: Commit changes 24 | id: commit 25 | # There may not be any updates to commit/push 26 | continue-on-error: true 27 | run: | 28 | git config user.name 'phylum-bot' 29 | git config user.email '69485888+phylum-bot@users.noreply.github.com' 30 | git commit -a -m "Bump dependencies" 31 | git push --force origin HEAD:${{ env.UPDATE_BRANCH_NAME }} 32 | 33 | - name: Create Pull Request 34 | id: pr 35 | if: ${{ steps.commit.outcome == 'success' }} 36 | # The PR may already exist (e.g., created in previous week and not merged yet) so we 37 | # allow it here and check in the next step so workflow failures will be extraordinary 38 | continue-on-error: true 39 | uses: actions/github-script@v8 40 | with: 41 | github-token: ${{ secrets.GH_RELEASE_PAT }} 42 | script: | 43 | const response = await github.rest.pulls.create({ 44 | owner: context.repo.owner, 45 | repo: context.repo.repo, 46 | head: "${{ env.UPDATE_BRANCH_NAME }}", 47 | base: context.ref, 48 | title: "Bump dependencies", 49 | body: "Bump dependencies for all SemVer-compatible updates.", 50 | }); 51 | console.log(response); 52 | 53 | - name: Verify PR exists 54 | if: ${{ steps.pr.outcome == 'failue' }} 55 | env: 56 | GH_TOKEN: ${{ github.token }} 57 | run: gh pr view ${{ env.UPDATE_BRANCH_NAME }} 58 | -------------------------------------------------------------------------------- /tests/fixtures/locator.spdx: -------------------------------------------------------------------------------- 1 | ##Document Header 2 | SPDXVersion: SPDX-2.2 3 | DataLicense: CC0-1.0 4 | SPDXID: SPDXRef-DOCUMENT 5 | DocumentName: localmaven-SBOM 6 | DocumentNamespace: https://localmaven 7 | Creator: Tool: github.com/phylum-dev/cli 8 | Created: 2023-04-04T20:15:57Z 9 | CreatorComment: SBOM Document for the localmaven project from Phylum, Inc. 10 | 11 | ## 12 | ##Packages 13 | ## 14 | PackageName: org.jruby:jruby-complete 15 | SPDXID: SPDXRef-org.jruby:jruby-complete-9.3.7.0 16 | PackageVersion: 9.3.7.0 17 | PackageDownloadLocation: https://repo1.maven.org/maven2/org/jruby/jruby-complete/9.3.7.0/jruby-complete-9.3.7.0-sources.jar 18 | FilesAnalyzed: true 19 | PackageHomePage: https://central.sonatype.com/artifact/org.jruby/jruby-complete/9.3.7.0 20 | PackageLicenseConcluded: NOASSERTION 21 | PackageLicenseDeclared: GPL-2.0,LGPL-2.1,EPL-2.0 22 | PackageCopyrightText: NOASSERTION 23 | PackageSupplier: NOASSERTION 24 | ExternalRef: PACKAGE-MANAGER maven org.jruby:jruby-complete:9.3.7.0 25 | Relationship: SPDXRef-org.jruby:jruby-core-9.3.7.0 PREREQUISITE_FOR SPDXRef-org.jruby:jruby-complete-9.3.7.0 26 | Relationship: SPDXRef-org.jruby:jruby-stdlib-9.3.7.0 PREREQUISITE_FOR SPDXRef-org.jruby:jruby-complete-9.3.7.0 27 | 28 | 29 | ## 30 | ##Packages 31 | ## 32 | PackageName: org.jruby:jruby-complete 33 | SPDXID: SPDXRef-org.jruby:jruby-complete-9.2.1.0 34 | PackageVersion: 9.2.1.0 35 | PackageDownloadLocation: https://repo1.maven.org/maven2/org/jruby/jruby-complete/9.2.1.0/jruby-complete-9.2.1.0-sources.jar 36 | FilesAnalyzed: true 37 | PackageHomePage: https://central.sonatype.com/artifact/org.jruby/jruby-complete/9.2.1.0 38 | PackageLicenseConcluded: NOASSERTION 39 | PackageLicenseDeclared: 40 | PackageCopyrightText: NOASSERTION 41 | PackageSupplier: NOASSERTION 42 | ExternalRef: PACKAGE-MANAGER maven org.jruby:jruby-complete:9.2.1.0 43 | Relationship: SPDXRef-org.jruby:jruby-core-${project.version} PREREQUISITE_FOR SPDXRef-org.jruby:jruby-complete-9.2.1.0 44 | Relationship: SPDXRef-org.jruby:jruby-stdlib-${project.version} PREREQUISITE_FOR SPDXRef-org.jruby:jruby-complete-9.2.1.0 45 | 46 | -------------------------------------------------------------------------------- /vulnreach_types/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Types for the vulnerability reachability API. 2 | 3 | use std::collections::HashSet; 4 | 5 | use serde::{Deserialize, Serialize}; 6 | 7 | /// Identified import vulnerability. 8 | #[derive(Serialize, Deserialize, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] 9 | pub struct Vulnerability { 10 | pub name: String, 11 | pub summary: String, 12 | /// Array storing the reachability path through each affected dependency. 13 | /// 14 | /// # Example: 15 | /// 16 | /// ```ignore 17 | /// // Packages reduced to their name for brevity. 18 | /// [ 19 | /// [server, http, vulnerable], 20 | /// [client, http, vulnerable], 21 | /// ] 22 | /// ``` 23 | pub vulnerable_dependencies: Vec>, 24 | } 25 | 26 | /// Dependency package. 27 | #[derive(Serialize, Deserialize, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] 28 | pub struct Package { 29 | pub name: String, 30 | pub version: String, 31 | /// Path taken through this dependency to reach the next vulnerable node. 32 | pub path: Vec, 33 | } 34 | 35 | /// Import usage location. 36 | #[derive(Serialize, Deserialize, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] 37 | pub struct Callsite { 38 | pub file: String, 39 | pub start: (usize, usize), 40 | pub end: (usize, usize), 41 | pub text: String, 42 | } 43 | 44 | /// A reachability analysis job. 45 | #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)] 46 | pub struct Job { 47 | /// Job ID for the Phylum issue analysis. 48 | pub analysis_job_id: String, 49 | /// The list of transitive dependencies for the user's project. 50 | pub dependencies: HashSet, 51 | /// The list of packages directly imported by the user. 52 | pub imported_packages: HashSet, 53 | } 54 | 55 | /// A globally unique package. 56 | #[derive(Serialize, Deserialize, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] 57 | pub struct JobPackage { 58 | pub name: String, 59 | pub version: String, 60 | pub ecosystem: String, 61 | } 62 | -------------------------------------------------------------------------------- /docs/extensions/extension_overview.md: -------------------------------------------------------------------------------- 1 | # Phylum CLI Extensions 2 | 3 | ## Overview 4 | 5 | Phylum CLI extensions are optional plugins for the CLI which provide additional 6 | functionality in a modular fashion. 7 | 8 | Extensions are executed in a [Deno] JavaScript runtime and have access to 9 | Phylum's API for commonly used operations. The capability-based permission 10 | system, together with Deno's sandbox, ensures that extensions can only do what 11 | they're supposed to. 12 | 13 | [Deno]: https://deno.land/ 14 | 15 | ## Usage 16 | 17 | If you're interested in using existing Phylum CLI extensions, you can take a 18 | look at the [CLI's extension documentation]. 19 | 20 | [CLI's extension documentation]: ../commands/phylum_extension.md 21 | 22 | ## Writing Extensions 23 | 24 | * [Quickstart](./extension_quickstart.md) 25 | * [Manifest Format](./extension_manifest.md) 26 | * [Extension API](./extension_api.md) 27 | * [Example](./extension_example.md) 28 | * [Extension Sandboxing](./extension_sandboxing.md) 29 | * [Direct Phylum API Requests](./extension_rest_api.md) 30 | 31 | > **TIP:** More info 32 | > 33 | > Additional how-to articles for the extension framework can be found 34 | > [here](https://dev.to/phylum). 35 | 36 | ## Official Extensions 37 | 38 | Official Phylum CLI extensions can be found [on GitHub]. These are a great place 39 | to get started if you want to try out some CLI extensions or write your own. 40 | 41 | [on GitHub]: https://github.com/phylum-dev/cli/tree/main/extensions 42 | 43 | Additionally, many of the official extensions are distributed with the Phylum 44 | CLI and should already be available for use. The pre-installed extensions are: 45 | 46 | * [`npm`](https://github.com/phylum-dev/cli/tree/main/extensions/npm) 47 | * [`pip`](https://github.com/phylum-dev/cli/tree/main/extensions/pip) 48 | * [`poetry`](https://github.com/phylum-dev/cli/tree/main/extensions/poetry) 49 | * [`yarn`](https://github.com/phylum-dev/cli/tree/main/extensions/yarn) 50 | * [`bundle`](https://github.com/phylum-dev/cli/tree/main/extensions/bundle) 51 | * [`cargo`](https://github.com/phylum-dev/cli/tree/main/extensions/cargo) 52 | -------------------------------------------------------------------------------- /doc_templates/README.md: -------------------------------------------------------------------------------- 1 | # CLI documentation templates 2 | 3 | These templates provide extra information for the automatically generated CLI 4 | documentation. 5 | 6 | ## Adding new templates 7 | 8 | By default, no template is necessary. If you want to modify the automatically 9 | generated CLI documentation, you can lookup the filename for the specific 10 | command in `../docs/commands/` and add a file with identical name to this 11 | directory to use as a template. 12 | 13 | Use the `default.md` file as a starting point, which provides the default 14 | template used when no override is present. 15 | 16 | ## Placeholders 17 | 18 | Some placeholders are available to templates, these will be replaced 19 | automatically with their values during documentation generation. 20 | 21 | These placeholders are currently supported: 22 | 23 | | Name | Description | 24 | | ---------------- | -------------------------------------------------------------------------------------- | 25 | | `{PH-HEADER}` | Docusaurus metadata header; omit this when manually overriding the header | 26 | | `{PH-TITLE}` | Command title (i.e. `phylum init`); use this for the Docusaurus metadata header title | 27 | | `{PH-MARKDOWN}` | Automatically generated command documentation; this should be present in all templates | 28 | 29 | ## Docusaurus metadata 30 | 31 | Some additional metadata can be specified by overriding the Docusaurus header. 32 | Available fields can be found in the [Docusaurus docs]. 33 | 34 | [Docusaurus docs]: https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-docs#markdown-front-matter 35 | 36 | ## Adding links 37 | 38 | Markdown links to other docs should be provided as relative file paths, with 39 | extensions. This is the [recommended approach from Docusaurus][docu_links]. 40 | 41 | The relative structure is as found in the [documentation repository][doc_repo]. 42 | All docs are stored or aggregated (via git submodules and symlinks) to the 43 | `docs` subdirectory there, which forms the root/anchor of relative paths. 44 | 45 | [docu_links]: https://docusaurus.io/docs/markdown-features/links 46 | [doc_repo]: https://github.com/phylum-dev/documentation 47 | -------------------------------------------------------------------------------- /tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # usage: ./tag.sh [BRANCH] 4 | # Tag the latest release commit (i.e., a commit with a "Release-Version" trailer) 5 | # 6 | # This script should be run on the default branch after a version bump (using bump_version.sh) has 7 | # been merged to the default branch. It will find and tag the latest release commit. If no branch 8 | # argument is provided, the search will begin from HEAD. 9 | # 10 | # Note: After the commit is found, a check is performed to ensure that the commit is reachable from 11 | # the default branch on the remote (origin/HEAD). To skip this check, set SKIP_ORIGIN_HEAD_CHECK=1 12 | 13 | set -eu 14 | 15 | SOURCE_BRANCH=HEAD 16 | if [ -n "${1:-}" ]; then 17 | SOURCE_BRANCH="$1" 18 | fi 19 | 20 | echo "Searching for latest release commit from ${SOURCE_BRANCH}" 21 | 22 | TAG_COMMIT=$(git rev-list -i --grep='^release-version:' -1 "${SOURCE_BRANCH}") 23 | if [ -z "${TAG_COMMIT}" ]; then 24 | echo "No release commit found!" >&2 25 | exit 1 26 | fi 27 | 28 | TAG=$(git log --pretty="format:%(trailers:key=release-version,valueonly)" -1 "${TAG_COMMIT}") 29 | 30 | if git show "tags/${TAG}" > /dev/null 2>&1; then 31 | echo "Tag ${TAG} already exists!" >&2 32 | exit 1 33 | fi 34 | 35 | echo "Tagging this commit:" 36 | git log --oneline -1 "${TAG_COMMIT}" 37 | 38 | if [ -z "${SKIP_ORIGIN_HEAD_CHECK:-}" ]; then 39 | # Check that the tag is being created on the default branch 40 | git fetch origin >/dev/null 41 | if ! git merge-base --is-ancestor "${TAG_COMMIT}" origin/HEAD; then 42 | echo "WARNING! You are about to tag a commit that is not on the default branch!" 43 | echo "Unless you are patching an old version, this is probably not what you want!" 44 | 45 | printf "Are you sure? [y/N] " 46 | read -r yn 47 | if [ "${yn}" != "y" ] && [ "${yn}" != "Y" ]; then 48 | echo "Aborting tag" 49 | exit 1 50 | fi 51 | fi 52 | fi 53 | 54 | git tag --sign -m "Phylum CLI ${TAG}" "${TAG}" "${TAG_COMMIT}" 55 | 56 | printf "\nOutput of the command: git show %s\n" "${TAG}" 57 | git show "${TAG}" 58 | 59 | cat << __instructions__ 60 | 61 | Successfully created tag! 62 | Run the following command manually to push the new tag: 63 | 64 | git push origin ${TAG} 65 | 66 | __instructions__ 67 | -------------------------------------------------------------------------------- /cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | use std::process; 2 | 3 | pub mod auth; 4 | pub mod exception; 5 | #[cfg(feature = "extensions")] 6 | pub mod extensions; 7 | pub mod find_dependency_files; 8 | pub mod firewall; 9 | pub mod group; 10 | pub mod init; 11 | pub mod jobs; 12 | pub mod org; 13 | pub mod packages; 14 | pub mod parse; 15 | pub mod project; 16 | #[cfg(unix)] 17 | pub mod sandbox; 18 | pub mod status; 19 | #[cfg(feature = "selfmanage")] 20 | pub mod uninstall; 21 | 22 | /// Shorthand type for Result whose ok value is CommandValue 23 | pub type CommandResult = anyhow::Result; 24 | 25 | /// Unique exit code values. 26 | #[derive(Copy, Clone)] 27 | pub enum ExitCode { 28 | Ok, 29 | Generic, 30 | NotAuthenticated, 31 | AuthenticationFailure, 32 | PackageNotFound, 33 | AlreadyExists, 34 | NoHistoryFound, 35 | JsError, 36 | ConfirmationFailed, 37 | NotFound, 38 | InvalidTokenExpiration, 39 | ManifestWithoutGeneration, 40 | UnknownManifestFormat, 41 | MissingOrg, 42 | FailedPolicy, 43 | SandboxStart, 44 | SandboxStartCollision, 45 | Custom(i32), 46 | } 47 | 48 | impl ExitCode { 49 | /// Terminate the application with this exit code. 50 | pub fn exit(&self) -> ! { 51 | process::exit(self.into()); 52 | } 53 | } 54 | 55 | impl From<&ExitCode> for i32 { 56 | fn from(code: &ExitCode) -> Self { 57 | match code { 58 | ExitCode::Ok => 0, 59 | ExitCode::Generic => 1, 60 | ExitCode::NotAuthenticated => 10, 61 | ExitCode::AuthenticationFailure => 11, 62 | ExitCode::PackageNotFound => 12, 63 | ExitCode::AlreadyExists => 14, 64 | ExitCode::NoHistoryFound => 15, 65 | ExitCode::JsError => 16, 66 | ExitCode::ConfirmationFailed => 17, 67 | ExitCode::NotFound => 18, 68 | ExitCode::InvalidTokenExpiration => 19, 69 | ExitCode::ManifestWithoutGeneration => 20, 70 | ExitCode::UnknownManifestFormat => 21, 71 | ExitCode::MissingOrg => 22, 72 | ExitCode::FailedPolicy => 100, 73 | ExitCode::SandboxStart => 117, 74 | ExitCode::SandboxStartCollision => 118, 75 | ExitCode::Custom(code) => *code, 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /docs/supported_lockfiles.md: -------------------------------------------------------------------------------- 1 | # Supported Lockfiles 2 | 3 | The Phylum CLI supports processing many different lockfiles: 4 | 5 | | Lockfile type | Lockfiles | 6 | | ------------- | ---------------------------------------------------------------------- | 7 | | `npm` | `package-lock.json`
`npm-shrinkwrap.json` | 8 | | `yarn` | `yarn.lock` (Version 1 + 2) | 9 | | `pnpm` | `pnpm-lock.yaml` | 10 | | `pip` | `requirements*.txt` | 11 | | `pipenv` | `Pipfile.lock` | 12 | | `poetry` | `poetry.lock` (Version 1 + 2) | 13 | | `gem` | `Gemfile.lock` | 14 | | `msbuild` | `*.csproj` | 15 | | `nugetlock` | `packages.lock.json`
`packages.*.lock.json` | 16 | | `nugetconfig` | `packages.config`
`packages.*.config` | 17 | | `mvn` | `effective-pom.xml` | 18 | | `gradle` | `gradle.lockfile`
`gradle/dependency-locks/*.lockfile` | 19 | | `go` | `go.sum` | 20 | | `gomod` | `go.mod` | 21 | | `cargo` | `Cargo.lock` | 22 | | `spdx` | `*.spdx.json`
`*.spdx.yaml`
`*.spdx.yml`
`*.spdx` | 23 | | `cyclonedx` | `*bom.json`
`*bom.xml` | 24 | 25 | --- 26 | 27 | > **NOTE:** 28 | > 29 | > The lockfile type will be automatically detected based on the filename. 30 | > 31 | > If needed, this can be overridden with the `--type` (`-t`) option. 32 | 33 | --- 34 | 35 | > **TIP:** Manifest Support 36 | > 37 | > Lockfiles can also automatically be generated for certain manifest files. 38 | > See [lockfile generation](./lockfile_generation.md) for details. 39 | -------------------------------------------------------------------------------- /tests/fixtures/packages.lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "dependencies": { 4 | ".NETFramework,Version=v4.8": { 5 | "Microsoft.Windows.SDK.Contracts": { 6 | "type": "Direct", 7 | "requested": "[10.0.22621.755, )", 8 | "resolved": "10.0.22621.755", 9 | "contentHash": "J1mpz9jekt87ZcbsHxU+iMvJIVJSMkTpeAftudx5AYsucPSxaQxlq3ZM2X8lVyRSdlMphtlfHfoTDKcMReIzdA==", 10 | "dependencies": { 11 | "System.Runtime.InteropServices.WindowsRuntime": "4.3.0", 12 | "System.Runtime.WindowsRuntime": "4.6.0", 13 | "System.Runtime.WindowsRuntime.UI.Xaml": "4.6.0" 14 | } 15 | }, 16 | "SSH.NET": { 17 | "type": "Transitive", 18 | "resolved": "2020.0.2", 19 | "contentHash": "G0dNlTBAM00KZXv1wWVwgg26d9/METcM6qWBpNQwllzQmmbu+Zu+FS1L1X4fFgGdPu3e8k9mmTBu6SwtQ0614g==" 20 | }, 21 | "example.helpers": { 22 | "type": "Project", 23 | "dependencies": { 24 | "SSH.NET": "[2020.0.2, )" 25 | } 26 | }, 27 | "Microsoft.Build.Tasks.Git": { 28 | "type": "Build", 29 | "requested": "[1.1.1, )", 30 | "resolved": "1.1.1", 31 | "contentHash": "AT3HlgTjsqHnWpBHSNeR0KxbLZD7bztlZVj7I8vgeYG9SYqbeFGh0TM/KVtC6fg53nrWHl3VfZFvb5BiQFcY6Q==" 32 | }, 33 | "System.Buffers": { 34 | "type": "Platform", 35 | "requested": "[4.5.1, )", 36 | "resolved": "4.5.1", 37 | "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" 38 | }, 39 | "Microsoft.CodeAnalysis.FxCopAnalyzers": { 40 | "type": "Tool", 41 | "requested": "[3.3.0, )", 42 | "resolved": "3.3.0", 43 | "contentHash": "EcnaSsPTqx2MGnHrmWOD0ugbuuqVT8iICqSqPzi45V5/MA1LjUNb0kwgcxBGqizV1R+WeBK7/Gw25Jzkyk9bIw==" 44 | } 45 | }, 46 | ".NETFramework,Version=v4.8/win": { 47 | "Microsoft.SourceLink.GitHub": { 48 | "type": "Direct", 49 | "requested": "[1.1.1, )", 50 | "resolved": "1.1.1", 51 | "contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==", 52 | "dependencies": { 53 | "Microsoft.Build.Tasks.Git": "1.1.1", 54 | "Microsoft.SourceLink.Common": "1.1.1" 55 | } 56 | } 57 | }, 58 | ".NETFramework,Version=v4.8/win-arm64": {}, 59 | ".NETFramework,Version=v4.8/win-x64": {}, 60 | ".NETFramework,Version=v4.8/win-x86": {} 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lockfile_generator/src/pnpm.rs: -------------------------------------------------------------------------------- 1 | //! JavaScript pnpm ecosystem. 2 | 3 | use std::ffi::OsStr; 4 | use std::path::{Path, PathBuf}; 5 | use std::process::Command; 6 | use std::{env, fs}; 7 | 8 | use crate::{Error, Generator, Result}; 9 | 10 | const WORKSPACE_MANIFEST_FILENAME: &str = "pnpm-workspace.yaml"; 11 | const WORKSPACE_DIR_ENV_VAR: &str = "NPM_CONFIG_WORKSPACE_DIR"; 12 | 13 | pub struct Pnpm; 14 | 15 | impl Generator for Pnpm { 16 | // Based on PNPM's implementation: 17 | // https://github.com/pnpm/pnpm/blob/98377afd3452d92183e4b643a8b122887c0406c3/workspace/find-workspace-dir/src/index.ts 18 | fn lockfile_path(&self, manifest_path: &Path) -> Result { 19 | let project_path = manifest_path 20 | .parent() 21 | .ok_or_else(|| Error::InvalidManifest(manifest_path.to_path_buf()))?; 22 | 23 | // Get project root from env variable. 24 | let workspace_dir_env = env::var_os(WORKSPACE_DIR_ENV_VAR) 25 | .or_else(|| env::var_os(WORKSPACE_DIR_ENV_VAR.to_lowercase())) 26 | .map(PathBuf::from); 27 | 28 | // Fallback to recursive search for `WORKSPACE_MANIFEST_FILENAME`. 29 | let workspace_root = workspace_dir_env.or_else(|| find_workspace_root(project_path)); 30 | 31 | // Fallback to non-workspace location. 32 | let root = workspace_root.unwrap_or_else(|| project_path.into()); 33 | 34 | Ok(root.join("pnpm-lock.yaml")) 35 | } 36 | 37 | fn command(&self, _manifest_path: &Path) -> Command { 38 | let mut command = Command::new("pnpm"); 39 | command.args(["install", "--lockfile-only", "--ignore-scripts"]); 40 | command 41 | } 42 | 43 | fn tool(&self) -> &'static str { 44 | "pnpm" 45 | } 46 | 47 | fn check_prerequisites(&self, manifest_path: &Path) -> Result<()> { 48 | if manifest_path.file_name() != Some(OsStr::new("package.json")) { 49 | Err(Error::InvalidManifest(manifest_path.to_path_buf())) 50 | } else { 51 | Ok(()) 52 | } 53 | } 54 | } 55 | 56 | /// Find PNPM workspace root. 57 | fn find_workspace_root(path: &Path) -> Option { 58 | for path in path.ancestors() { 59 | let dir = fs::read_dir(path).ok()?; 60 | 61 | for dir_entry in dir.into_iter().flatten().map(|entry| entry.path()) { 62 | if dir_entry.file_name().is_some_and(|name| name == WORKSPACE_MANIFEST_FILENAME) { 63 | return Some(path.into()); 64 | } 65 | } 66 | } 67 | 68 | None 69 | } 70 | -------------------------------------------------------------------------------- /cli/src/auth/jwt.rs: -------------------------------------------------------------------------------- 1 | /// JWT token parsing. 2 | use std::collections::HashSet; 3 | use std::string::FromUtf8Error; 4 | 5 | use base64::engine::general_purpose::STANDARD; 6 | use base64::prelude::*; 7 | use base64::DecodeError; 8 | use serde::Deserialize; 9 | use serde_json::Error as JsonError; 10 | 11 | /// Get user roles from a bearer token without performing validation. 12 | pub fn user_roles(bearer: &str) -> Result, JwtError> { 13 | // Extract the base64 payload. 14 | let (_, payload_base64, _) = parts(bearer)?; 15 | 16 | // Decode the payload. 17 | let payload_bytes = STANDARD.decode(payload_base64)?; 18 | let payload_text = String::from_utf8(payload_bytes)?; 19 | 20 | // Parse as JSON. 21 | let payload: PhylumBearer = serde_json::from_str(&payload_text)?; 22 | 23 | Ok(payload.realm_access.roles) 24 | } 25 | 26 | /// Split a bearer token into header/payload/signature. 27 | fn parts(bearer: &str) -> Result<(&str, &str, &str), JwtError> { 28 | let mut parts = bearer.split('.'); 29 | 30 | let header = parts.next().unwrap(); 31 | let payload = parts.next().ok_or(JwtError::MissingPayload)?; 32 | let signature = parts.next().ok_or(JwtError::MissingSignature)?; 33 | 34 | match parts.next() { 35 | Some(_) => Err(JwtError::UnexpectedComponent), 36 | None => Ok((header, payload, signature)), 37 | } 38 | } 39 | 40 | /// Available Phylum JWT realm roles. 41 | #[derive(Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] 42 | pub enum RealmRole { 43 | #[serde(rename = "pro-account")] 44 | Pro, 45 | #[serde(rename = "vulnreach")] 46 | Vulnreach, 47 | #[serde(other)] 48 | Unknown, 49 | } 50 | 51 | /// JWT parsing error. 52 | #[derive(thiserror::Error, Debug)] 53 | pub enum JwtError { 54 | #[error("JWT missing payload part")] 55 | MissingPayload, 56 | #[error("JWT missing signature part")] 57 | MissingSignature, 58 | #[error("JWT cannot have more than 3 parts")] 59 | UnexpectedComponent, 60 | #[error("invalid base64")] 61 | InvalidBase64(#[from] DecodeError), 62 | #[error("invalid UTF-8")] 63 | InvalidUtf8(#[from] FromUtf8Error), 64 | #[error("invalid JSON")] 65 | InvalidJson(#[from] JsonError), 66 | } 67 | 68 | /// Partial Phylum JWT bearer payload. 69 | #[derive(Deserialize, Debug)] 70 | struct PhylumBearer { 71 | realm_access: RealmAccess, 72 | } 73 | 74 | /// Partial Phylum JWT realm access. 75 | #[derive(Deserialize, Debug)] 76 | struct RealmAccess { 77 | roles: HashSet, 78 | } 79 | -------------------------------------------------------------------------------- /cli/src/commands/packages.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use anyhow::Result; 4 | use clap::ArgMatches; 5 | use reqwest::StatusCode; 6 | 7 | use crate::api::PhylumApi; 8 | use crate::commands::{CommandResult, ExitCode}; 9 | use crate::filter::{Filter, FilterIssues}; 10 | use crate::format::Format; 11 | use crate::types::{PackageSpecifier, PackageSubmitResponse}; 12 | use crate::{print_user_failure, print_user_warning}; 13 | 14 | fn parse_package(matches: &ArgMatches) -> Result { 15 | // Read required options. 16 | let name = matches.get_one::("name").unwrap().to_string(); 17 | let version = matches.get_one::("version").unwrap().to_string(); 18 | let registry = matches.get_one::("package-type").unwrap().to_string(); 19 | 20 | Ok(PackageSpecifier { name, version, registry }) 21 | } 22 | 23 | /// Handle the subcommands for the `package` subcommand. 24 | pub async fn handle_get_package(api: &PhylumApi, matches: &clap::ArgMatches) -> CommandResult { 25 | let pretty_print = !matches.get_flag("json"); 26 | 27 | let pkg = parse_package(matches)?; 28 | let resp = match api.submit_package(&pkg).await { 29 | Ok(resp) => resp, 30 | Err(err) if err.status() == Some(StatusCode::NOT_FOUND) => { 31 | print_user_warning!("No matching package found."); 32 | return Ok(ExitCode::PackageNotFound); 33 | }, 34 | Err(err) => return Err(err.into()), 35 | }; 36 | 37 | match resp { 38 | PackageSubmitResponse::AlreadyProcessed(mut resp) if resp.complete => { 39 | match resp.pipeline_error.take().filter(|e| !e.is_empty()) { 40 | Some(_) => print_user_failure!( 41 | "Package analysis failed, please contact Phylum if this package exists." 42 | ), 43 | None => { 44 | let filter = 45 | matches.get_one::("filter").and_then(|v| Filter::from_str(v).ok()); 46 | if let Some(filter) = filter { 47 | resp.filter(&filter); 48 | } 49 | 50 | resp.write_stdout(pretty_print); 51 | }, 52 | } 53 | }, 54 | PackageSubmitResponse::New => { 55 | print_user_warning!( 56 | "Thank you for submitting this package. Please check back later for results." 57 | ); 58 | }, 59 | _ => { 60 | print_user_warning!( 61 | "Package is still processing. Please check back later for results." 62 | ); 63 | }, 64 | } 65 | 66 | Ok(ExitCode::Ok) 67 | } 68 | -------------------------------------------------------------------------------- /cli/src/commands/firewall.rs: -------------------------------------------------------------------------------- 1 | //! Subcommand `phylum firewall`. 2 | 3 | use std::str::FromStr; 4 | 5 | use clap::ArgMatches; 6 | use purl::{PackageType, Purl}; 7 | 8 | use crate::api::PhylumApi; 9 | use crate::commands::{CommandResult, ExitCode}; 10 | use crate::config::Config; 11 | use crate::format::Format; 12 | use crate::print_user_failure; 13 | use crate::types::{FirewallAction, FirewallLogFilter}; 14 | 15 | /// Handle `phylum firewall` subcommand. 16 | pub async fn handle_firewall( 17 | api: &PhylumApi, 18 | matches: &ArgMatches, 19 | config: Config, 20 | ) -> CommandResult { 21 | match matches.subcommand() { 22 | Some(("log", matches)) => handle_log(api, matches, config).await, 23 | _ => unreachable!("invalid clap configuration"), 24 | } 25 | } 26 | 27 | /// Handle `phylum firewall log` subcommand. 28 | pub async fn handle_log(api: &PhylumApi, matches: &ArgMatches, config: Config) -> CommandResult { 29 | let org = config.org(); 30 | let group = matches.get_one::("group").unwrap(); 31 | 32 | // Get log filter args. 33 | let package_type = matches.get_one::("package-type"); 34 | let action = matches.get_one::("action"); 35 | let before = matches.get_one::("before"); 36 | let after = matches.get_one::("after"); 37 | let purl = matches.get_one::("purl"); 38 | let limit = matches.get_one::("limit").unwrap(); 39 | 40 | // Parse PURL filter. 41 | let parsed_purl = purl.map(|purl| Purl::from_str(purl)); 42 | let (package_type, namespace, name, version) = match &parsed_purl { 43 | Some(Ok(purl)) => { 44 | (Some(*purl.package_type()), purl.namespace(), Some(purl.name()), purl.version()) 45 | }, 46 | Some(Err(err)) => { 47 | print_user_failure!("Could not parse purl {purl:?}: {err}"); 48 | return Ok(ExitCode::Generic); 49 | }, 50 | None => { 51 | let package_type = package_type.and_then(|pt| PackageType::from_str(pt).ok()); 52 | (package_type, None, None, None) 53 | }, 54 | }; 55 | 56 | // Construct the filter. 57 | let filter = FirewallLogFilter { 58 | namespace, 59 | version, 60 | name, 61 | before: before.map(String::as_str), 62 | after: after.map(String::as_str), 63 | limit: Some(*limit as i32), 64 | ecosystem: package_type, 65 | action: action.copied(), 66 | }; 67 | 68 | let response = api.firewall_log(org, group, filter).await?; 69 | 70 | let pretty = !matches.get_flag("json"); 71 | response.data.write_stdout(pretty); 72 | 73 | Ok(ExitCode::Ok) 74 | } 75 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | The Phylum command line interface (CLI) allows users to submit their project package dependencies to [Phylum's](https://app.phylum.io) API for analysis. Currently [pre-built binaries](https://github.com/phylum-dev/cli/releases) for Linux and macOS are available. On Windows, we recommend using the Linux binaries under [WSL](https://learn.microsoft.com/en-us/windows/wsl/). See the [alternate installation methods](./alternate_install.md) for more options. 4 | 5 | ## Install `phylum` CLI 6 | 7 | ### Install on Linux 8 | 9 | Install on Linux with the following command: 10 | 11 | ```sh 12 | curl https://sh.phylum.io/ | sh - 13 | ``` 14 | 15 | ### Install on macOS 16 | 17 | On macOS, we recommend installing phylum with [Homebrew](https://brew.sh/): 18 | 19 | ```sh 20 | brew install phylum 21 | ``` 22 | 23 | > **Note:** When using Homebrew, [official extensions][] must be installed separately. 24 | 25 | [official extensions]: https://github.com/phylum-dev/cli/tree/main/extensions 26 | 27 | ## Quickstart for Linux or macOS 28 | 29 | 1. [Register](./commands/phylum_auth_register.md) for an account (if you don't already have one) 30 | 31 | ```sh 32 | phylum auth register 33 | ``` 34 | 35 | 2. [Authenticate](./commands/phylum_auth_login.md) with Phylum 36 | 37 | ```sh 38 | phylum auth login 39 | ``` 40 | 41 | 3. [Setup your Phylum project](./commands/phylum_init.md) in your project directory 42 | 43 | ```sh 44 | phylum init 45 | ``` 46 | 47 | 4. [Submit your lockfiles and manifests](./commands/phylum_analyze.md) to [analyze dependencies](./analyzing_dependencies.md) 48 | 49 | ```sh 50 | phylum analyze 51 | ``` 52 | 53 | 5. (Optional) View the analysis results in the [Phylum UI](https://app.phylum.io/auth/login) 54 | 55 | --- 56 | ## License 57 | 58 | Copyright (C) 2022 Phylum, Inc. 59 | 60 | This program is free software: you can redistribute it and/or modify it under 61 | the terms of the GNU General Public License as published by the Free Software 62 | Foundation, either version 3 of the License or any later version. 63 | 64 | This program is distributed in the hope that it will be useful, but WITHOUT 65 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 66 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 67 | 68 | You should have received a copy of the GNU General Public License along with 69 | this program. If not, see or write to 70 | `support@veracode.com`. 71 | 72 | ## Questions/Issues 73 | 74 | Please contact Phylum with any questions or issues using the CLI tool. 75 | 76 | Email: `dl-phylum-engineering@veracode.com` 77 | -------------------------------------------------------------------------------- /lockfile/src/parsers/go_sum.rs: -------------------------------------------------------------------------------- 1 | use nom::branch::alt; 2 | use nom::bytes::complete::{tag, take_until}; 3 | use nom::character::complete::{alphanumeric1, line_ending, space0, space1}; 4 | use nom::combinator::{opt, recognize}; 5 | use nom::multi::{many0, many1}; 6 | use nom::sequence::preceded; 7 | use nom::Parser; 8 | use phylum_types::types::package::PackageType; 9 | 10 | use crate::parsers::IResult; 11 | use crate::{Package, PackageVersion}; 12 | 13 | pub fn parse(input: &str) -> IResult<&str, Vec> { 14 | let (input, pkgs) = many0(package).parse(input)?; 15 | 16 | let pkgs = pkgs 17 | .into_iter() 18 | .filter(|p| match &p.version { 19 | PackageVersion::FirstParty(v) => !v.ends_with("/go.mod"), 20 | _ => false, 21 | }) 22 | .collect(); 23 | 24 | Ok((input, pkgs)) 25 | } 26 | 27 | fn package(input: &str) -> IResult<&str, Package> { 28 | let (input, name) = package_name(input)?; 29 | let (input, version) = package_version(input)?; 30 | let (input, _hash) = package_hash(input)?; 31 | 32 | let package = Package { 33 | name: name.to_string(), 34 | version: PackageVersion::FirstParty(version.to_string()), 35 | package_type: PackageType::Golang, 36 | }; 37 | 38 | Ok((input, package)) 39 | } 40 | 41 | fn package_name(input: &str) -> IResult<&str, &str> { 42 | // Take away any leading whitespace. 43 | let (input, _) = space0(input)?; 44 | 45 | // The package name will be everything up until a space. 46 | recognize(take_until(" ")).parse(input) 47 | } 48 | 49 | fn package_version(input: &str) -> IResult<&str, &str> { 50 | // Take away any leading whitespace. 51 | let (input, _) = space0(input)?; 52 | 53 | // Accept all of `v[a-zA-Z0-9.+-]+` with an optional "/go.mod" suffix. 54 | let (input, version) = recognize(( 55 | tag("v"), 56 | many1(alt((alphanumeric1, tag("."), tag("-"), tag("+")))), 57 | opt(tag("/go.mod")), 58 | )) 59 | .parse(input)?; 60 | 61 | // Expect at least one whitespace after version. 62 | let (input, _) = space1(input)?; 63 | 64 | Ok((input, version)) 65 | } 66 | 67 | fn package_hash(input: &str) -> IResult<&str, &str> { 68 | // Take away any leading whitespace. 69 | let (input, _) = space0(input)?; 70 | 71 | // Base64 parser for package hash. 72 | let base64_parser = recognize(many1(alt((alphanumeric1, tag("+"), tag("/"), tag("="))))); 73 | 74 | // Parse base64 hash with `h1:` prefix. 75 | let (input, hash) = preceded(tag("h1:"), base64_parser).parse(input)?; 76 | 77 | // Expect EOL. 78 | let (input, _) = line_ending(input)?; 79 | 80 | Ok((input, hash)) 81 | } 82 | -------------------------------------------------------------------------------- /tests/fixtures/sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(NUnitRuntimeFrameworks) 5 | NUnit.Framework 6 | 7 | 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 6.2.10 20 | 21 | 22 | 1.5.0 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /cli/src/print.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use anyhow::{anyhow, Result}; 4 | use clap::Command; 5 | use console::style; 6 | use prettytable::format; 7 | use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; 8 | 9 | #[macro_export] 10 | macro_rules! print_user_success { 11 | ($($tts:tt)*) => {{ 12 | print!("✅ "); 13 | println!($($tts)*); 14 | }} 15 | } 16 | 17 | #[macro_export] 18 | macro_rules! print_user_warning { 19 | ($($tts:tt)*) => {{ 20 | eprint!("⚠️ "); 21 | eprintln!($($tts)*); 22 | }} 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! print_user_failure { 27 | ($($tts:tt)*) => {{ 28 | eprint!("❗ "); 29 | eprintln!($($tts)*); 30 | }} 31 | } 32 | 33 | /// Prints a verbose message informing the user that an update is available. 34 | pub fn print_update_message() { 35 | eprintln!("---------------- {} ----------------\n", style("Update Available").cyan()); 36 | eprintln!("A new version of the Phylum CLI is available. Run"); 37 | eprintln!("\n\t{}\n\nto update to the latest version!\n", style("phylum update").blue()); 38 | eprintln!("{:-^50}\n\n", ""); 39 | } 40 | 41 | pub fn print_sc_help(mut app: &mut Command, subcommands: &[&str]) -> Result<()> { 42 | for subcommand in subcommands { 43 | match app.find_subcommand_mut(*subcommand) { 44 | Some(subcommand) => app = subcommand, 45 | // Subcommand doesn't exist; don't print anything. 46 | None => return Err(anyhow!("Subcommand '{subcommand}' does not exist")), 47 | } 48 | } 49 | 50 | app.print_help()?; 51 | 52 | Ok(()) 53 | } 54 | 55 | /// Limit a string to a specific length, using an ellipsis to indicate 56 | /// truncation. 57 | pub fn truncate(text: &str, max_length: usize) -> Cow<'_, str> { 58 | if text.width() > max_length { 59 | let mut len = 0; 60 | let truncated = text 61 | .chars() 62 | .take_while(|c| { 63 | len += c.width().unwrap_or(0); 64 | len < max_length 65 | }) 66 | .collect::() 67 | .trim_end() 68 | .to_owned() 69 | + "…"; 70 | Cow::Owned(truncated) 71 | } else { 72 | Cow::Borrowed(text) 73 | } 74 | } 75 | 76 | pub fn table_format(left_pad: usize, right_pad: usize) -> format::TableFormat { 77 | format::FormatBuilder::new() 78 | .column_separator(' ') 79 | .borders(' ') 80 | .separators( 81 | &[format::LinePosition::Top, format::LinePosition::Bottom], 82 | format::LineSeparator::new(' ', ' ', ' ', ' '), 83 | ) 84 | .padding(left_pad, right_pad) 85 | .build() 86 | } 87 | -------------------------------------------------------------------------------- /docs/commands/phylum_analyze.md: -------------------------------------------------------------------------------- 1 | # phylum analyze 2 | 3 | Submit a request for analysis to the processing system 4 | 5 | ```sh 6 | Usage: phylum analyze [OPTIONS] [DEPENDENCY_FILE]... 7 | ``` 8 | 9 | ## Arguments 10 | 11 | `[DEPENDENCY_FILE]` 12 |   Path to the dependency file to submit 13 | 14 | ## Options 15 | 16 | `-l`, `--label` `