mirror of
https://github.com/safedep/vet.git
synced 2025-12-11 01:01:10 -06:00
Add auth persistence
Add parser and models Add parser in scanner Add enrichment Use pointer for package ref Add work queue for concurrent enrichment Update enrich Misc refactoring Update README Refactored lockfile parsers Add analyzers Update json dumper Refactor scan
This commit is contained in:
parent
561408de8b
commit
ce10afab06
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,4 +13,6 @@
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
/vet
|
||||
/gen
|
||||
|
||||
28
Makefile
Normal file
28
Makefile
Normal file
@ -0,0 +1,28 @@
|
||||
all: clean setup vet
|
||||
|
||||
oapi-codegen-install:
|
||||
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.10.1
|
||||
|
||||
oapi-codegen:
|
||||
oapi-codegen -package insightapi -generate types ./api/insights-v1.yml > ./gen/insightapi/insights.types.go
|
||||
oapi-codegen -package insightapi -generate client ./api/insights-v1.yml > ./gen/insightapi/insights.client.go
|
||||
|
||||
setup:
|
||||
mkdir -p out gen/insightapi
|
||||
|
||||
vet: oapi-codegen
|
||||
go build
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test ./...
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -rf out
|
||||
-rm -rf gen
|
||||
|
||||
gosec:
|
||||
-docker run --rm -it -w /app/ -v `pwd`:/app/ securego/gosec \
|
||||
-exclude-dir=/app/gen -exclude-dir=/app/spec \
|
||||
/app/...
|
||||
38
README.md
38
README.md
@ -1,2 +1,36 @@
|
||||
# vet
|
||||
Tool for identifying software supply chain risks using Insights API
|
||||
# vet : The dependency vetting tool
|
||||
Tool for identifying software supply chain risks
|
||||
|
||||
## TL;DR
|
||||
|
||||
Build this repository
|
||||
|
||||
> Ensure `$(go env GOPATH)/bin` is in your `$PATH`
|
||||
|
||||
```bash
|
||||
make oapi-codegen-install && make
|
||||
```
|
||||
|
||||
Configure `vet` to use API Key to access [Insights API](#)
|
||||
|
||||
```bash
|
||||
vet auth configure
|
||||
```
|
||||
|
||||
> Alternatively pass the API key as environment to skip configuration
|
||||
|
||||
Run `vet` to identify risks
|
||||
|
||||
```bash
|
||||
vet scan
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Configuration
|
||||
|
||||
Insights API Key can be passed at runtime using environment variable
|
||||
|
||||
```bash
|
||||
VET_INSIGHTS_API_KEY=... vet scan
|
||||
```
|
||||
|
||||
780
api/insights-v1.yml
Normal file
780
api/insights-v1.yml
Normal file
@ -0,0 +1,780 @@
|
||||
openapi: 3.0.2
|
||||
info:
|
||||
title: SafeDep OSS Insights API
|
||||
contact:
|
||||
name: SafeDep API
|
||||
url: 'https://safedep.io'
|
||||
description: |
|
||||
The Insights API expose various metadata about OSS artifacts. Clients can
|
||||
query this API to gather the data required for rich policy decision making
|
||||
for various use-cases.
|
||||
version: 1.0.0
|
||||
servers:
|
||||
- url: 'https://{apiHost}/{apiBase}'
|
||||
variables:
|
||||
apiHost:
|
||||
default: api.safedep.io
|
||||
apiBase:
|
||||
default: insights/v1
|
||||
tags:
|
||||
- name: Package Meta Data
|
||||
description: Package meta data related operations
|
||||
- name: Infrastructure
|
||||
description: Infrastructure support operations
|
||||
paths:
|
||||
/healthz:
|
||||
get:
|
||||
description: Get health check status
|
||||
operationId: getHealthCheckStatus
|
||||
tags:
|
||||
- Infrastructure
|
||||
security: []
|
||||
responses:
|
||||
'200':
|
||||
description: Server is operational
|
||||
'/{ecosystem}/packages/{name}/versions/{version}':
|
||||
get:
|
||||
description: Get metadata for a package version
|
||||
operationId: getPackageVersionInsight
|
||||
tags:
|
||||
- Package Meta Data
|
||||
security:
|
||||
- api_key: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PackageVersionInsight'
|
||||
'404':
|
||||
description: Requested resource was not found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
'429':
|
||||
description: Rate limit block
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
'500':
|
||||
description: Failed due to internal server error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiError'
|
||||
parameters:
|
||||
- name: ecosystem
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Case insensitive ecosystem name
|
||||
enum:
|
||||
- Maven
|
||||
- RubyGems
|
||||
- Go
|
||||
- npm
|
||||
- PyPI
|
||||
- Cargo
|
||||
- NuGet
|
||||
- Linux
|
||||
- Debian
|
||||
- Github Actions
|
||||
- name: name
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: version
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
components:
|
||||
securitySchemes:
|
||||
api_key:
|
||||
type: apiKey
|
||||
name: Authorization
|
||||
in: header
|
||||
schemas:
|
||||
ApiError:
|
||||
type: object
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
description: A descriptive message about the error meant for developer consumption
|
||||
type:
|
||||
type: string
|
||||
description: An optional service or domain specific error group
|
||||
code:
|
||||
type: string
|
||||
description: An error code identifying the error
|
||||
params:
|
||||
type: object
|
||||
description: Optional error specific attributes
|
||||
additionalProperties:
|
||||
type: object
|
||||
properties:
|
||||
key:
|
||||
type: string
|
||||
value:
|
||||
type: string
|
||||
PackageVersion:
|
||||
type: object
|
||||
required:
|
||||
- ecosystem
|
||||
- name
|
||||
- version
|
||||
properties:
|
||||
ecosystem:
|
||||
type: string
|
||||
description: The ecosystem where this package belongs to
|
||||
name:
|
||||
type: string
|
||||
description: The name of the package
|
||||
version:
|
||||
type: string
|
||||
description: The version of the package
|
||||
License:
|
||||
type: string
|
||||
description: License SPDX code
|
||||
enum:
|
||||
- 0BSD
|
||||
- AAL
|
||||
- Abstyles
|
||||
- Adobe-2006
|
||||
- Adobe-Glyph
|
||||
- ADSL
|
||||
- AFL-1.1
|
||||
- AFL-1.2
|
||||
- AFL-2.0
|
||||
- AFL-2.1
|
||||
- AFL-3.0
|
||||
- Afmparse
|
||||
- AGPL-1.0
|
||||
- AGPL-1.0-only
|
||||
- AGPL-1.0-or-later
|
||||
- AGPL-3.0
|
||||
- AGPL-3.0-only
|
||||
- AGPL-3.0-or-later
|
||||
- Aladdin
|
||||
- AMDPLPA
|
||||
- AML
|
||||
- AMPAS
|
||||
- ANTLR-PD
|
||||
- ANTLR-PD-fallback
|
||||
- Apache-1.0
|
||||
- Apache-1.1
|
||||
- Apache-2.0
|
||||
- APAFML
|
||||
- APL-1.0
|
||||
- App-s2p
|
||||
- APSL-1.0
|
||||
- APSL-1.1
|
||||
- APSL-1.2
|
||||
- APSL-2.0
|
||||
- Arphic-1999
|
||||
- Artistic-1.0
|
||||
- Artistic-1.0-cl8
|
||||
- Artistic-1.0-Perl
|
||||
- Artistic-2.0
|
||||
- Baekmuk
|
||||
- Bahyph
|
||||
- Barr
|
||||
- Beerware
|
||||
- Bitstream-Vera
|
||||
- BitTorrent-1.0
|
||||
- BitTorrent-1.1
|
||||
- blessing
|
||||
- BlueOak-1.0.0
|
||||
- Borceux
|
||||
- BSD-1-Clause
|
||||
- BSD-2-Clause
|
||||
- BSD-2-Clause-FreeBSD
|
||||
- BSD-2-Clause-NetBSD
|
||||
- BSD-2-Clause-Patent
|
||||
- BSD-2-Clause-Views
|
||||
- BSD-3-Clause
|
||||
- BSD-3-Clause-Attribution
|
||||
- BSD-3-Clause-Clear
|
||||
- BSD-3-Clause-LBNL
|
||||
- BSD-3-Clause-Modification
|
||||
- BSD-3-Clause-No-Military-License
|
||||
- BSD-3-Clause-No-Nuclear-License
|
||||
- BSD-3-Clause-No-Nuclear-License-2014
|
||||
- BSD-3-Clause-No-Nuclear-Warranty
|
||||
- BSD-3-Clause-Open-MPI
|
||||
- BSD-4-Clause
|
||||
- BSD-4-Clause-Shortened
|
||||
- BSD-4-Clause-UC
|
||||
- BSD-Protection
|
||||
- BSD-Source-Code
|
||||
- BSL-1.0
|
||||
- BUSL-1.1
|
||||
- bzip2-1.0.5
|
||||
- bzip2-1.0.6
|
||||
- C-UDA-1.0
|
||||
- CAL-1.0
|
||||
- CAL-1.0-Combined-Work-Exception
|
||||
- Caldera
|
||||
- CATOSL-1.1
|
||||
- CC-BY-1.0
|
||||
- CC-BY-2.0
|
||||
- CC-BY-2.5
|
||||
- CC-BY-2.5-AU
|
||||
- CC-BY-3.0
|
||||
- CC-BY-3.0-AT
|
||||
- CC-BY-3.0-DE
|
||||
- CC-BY-3.0-IGO
|
||||
- CC-BY-3.0-NL
|
||||
- CC-BY-3.0-US
|
||||
- CC-BY-4.0
|
||||
- CC-BY-NC-1.0
|
||||
- CC-BY-NC-2.0
|
||||
- CC-BY-NC-2.5
|
||||
- CC-BY-NC-3.0
|
||||
- CC-BY-NC-3.0-DE
|
||||
- CC-BY-NC-4.0
|
||||
- CC-BY-NC-ND-1.0
|
||||
- CC-BY-NC-ND-2.0
|
||||
- CC-BY-NC-ND-2.5
|
||||
- CC-BY-NC-ND-3.0
|
||||
- CC-BY-NC-ND-3.0-DE
|
||||
- CC-BY-NC-ND-3.0-IGO
|
||||
- CC-BY-NC-ND-4.0
|
||||
- CC-BY-NC-SA-1.0
|
||||
- CC-BY-NC-SA-2.0
|
||||
- CC-BY-NC-SA-2.0-FR
|
||||
- CC-BY-NC-SA-2.0-UK
|
||||
- CC-BY-NC-SA-2.5
|
||||
- CC-BY-NC-SA-3.0
|
||||
- CC-BY-NC-SA-3.0-DE
|
||||
- CC-BY-NC-SA-3.0-IGO
|
||||
- CC-BY-NC-SA-4.0
|
||||
- CC-BY-ND-1.0
|
||||
- CC-BY-ND-2.0
|
||||
- CC-BY-ND-2.5
|
||||
- CC-BY-ND-3.0
|
||||
- CC-BY-ND-3.0-DE
|
||||
- CC-BY-ND-4.0
|
||||
- CC-BY-SA-1.0
|
||||
- CC-BY-SA-2.0
|
||||
- CC-BY-SA-2.0-UK
|
||||
- CC-BY-SA-2.1-JP
|
||||
- CC-BY-SA-2.5
|
||||
- CC-BY-SA-3.0
|
||||
- CC-BY-SA-3.0-AT
|
||||
- CC-BY-SA-3.0-DE
|
||||
- CC-BY-SA-4.0
|
||||
- CC-PDDC
|
||||
- CC0-1.0
|
||||
- CDDL-1.0
|
||||
- CDDL-1.1
|
||||
- CDL-1.0
|
||||
- CDLA-Permissive-1.0
|
||||
- CDLA-Permissive-2.0
|
||||
- CDLA-Sharing-1.0
|
||||
- CECILL-1.0
|
||||
- CECILL-1.1
|
||||
- CECILL-2.0
|
||||
- CECILL-2.1
|
||||
- CECILL-B
|
||||
- CECILL-C
|
||||
- CERN-OHL-1.1
|
||||
- CERN-OHL-1.2
|
||||
- CERN-OHL-P-2.0
|
||||
- CERN-OHL-S-2.0
|
||||
- CERN-OHL-W-2.0
|
||||
- checkmk
|
||||
- ClArtistic
|
||||
- CNRI-Jython
|
||||
- CNRI-Python
|
||||
- CNRI-Python-GPL-Compatible
|
||||
- COIL-1.0
|
||||
- Community-Spec-1.0
|
||||
- Condor-1.1
|
||||
- copyleft-next-0.3.0
|
||||
- copyleft-next-0.3.1
|
||||
- CPAL-1.0
|
||||
- CPL-1.0
|
||||
- CPOL-1.02
|
||||
- Crossword
|
||||
- CrystalStacker
|
||||
- CUA-OPL-1.0
|
||||
- Cube
|
||||
- curl
|
||||
- D-FSL-1.0
|
||||
- diffmark
|
||||
- DL-DE-BY-2.0
|
||||
- DOC
|
||||
- Dotseqn
|
||||
- DRL-1.0
|
||||
- DSDP
|
||||
- dvipdfm
|
||||
- ECL-1.0
|
||||
- ECL-2.0
|
||||
- eCos-2.0
|
||||
- EFL-1.0
|
||||
- EFL-2.0
|
||||
- eGenix
|
||||
- Elastic-2.0
|
||||
- Entessa
|
||||
- EPICS
|
||||
- EPL-1.0
|
||||
- EPL-2.0
|
||||
- ErlPL-1.1
|
||||
- etalab-2.0
|
||||
- EUDatagrid
|
||||
- EUPL-1.0
|
||||
- EUPL-1.1
|
||||
- EUPL-1.2
|
||||
- Eurosym
|
||||
- Fair
|
||||
- FDK-AAC
|
||||
- Frameworx-1.0
|
||||
- FreeBSD-DOC
|
||||
- FreeImage
|
||||
- FSFAP
|
||||
- FSFUL
|
||||
- FSFULLR
|
||||
- FSFULLRWD
|
||||
- FTL
|
||||
- GD
|
||||
- GFDL-1.1
|
||||
- GFDL-1.1-invariants-only
|
||||
- GFDL-1.1-invariants-or-later
|
||||
- GFDL-1.1-no-invariants-only
|
||||
- GFDL-1.1-no-invariants-or-later
|
||||
- GFDL-1.1-only
|
||||
- GFDL-1.1-or-later
|
||||
- GFDL-1.2
|
||||
- GFDL-1.2-invariants-only
|
||||
- GFDL-1.2-invariants-or-later
|
||||
- GFDL-1.2-no-invariants-only
|
||||
- GFDL-1.2-no-invariants-or-later
|
||||
- GFDL-1.2-only
|
||||
- GFDL-1.2-or-later
|
||||
- GFDL-1.3
|
||||
- GFDL-1.3-invariants-only
|
||||
- GFDL-1.3-invariants-or-later
|
||||
- GFDL-1.3-no-invariants-only
|
||||
- GFDL-1.3-no-invariants-or-later
|
||||
- GFDL-1.3-only
|
||||
- GFDL-1.3-or-later
|
||||
- Giftware
|
||||
- GL2PS
|
||||
- Glide
|
||||
- Glulxe
|
||||
- GLWTPL
|
||||
- gnuplot
|
||||
- GPL-1.0
|
||||
- GPL-1.0+
|
||||
- GPL-1.0-only
|
||||
- GPL-1.0-or-later
|
||||
- GPL-2.0
|
||||
- GPL-2.0+
|
||||
- GPL-2.0-only
|
||||
- GPL-2.0-or-later
|
||||
- GPL-2.0-with-autoconf-exception
|
||||
- GPL-2.0-with-bison-exception
|
||||
- GPL-2.0-with-classpath-exception
|
||||
- GPL-2.0-with-font-exception
|
||||
- GPL-2.0-with-GCC-exception
|
||||
- GPL-3.0
|
||||
- GPL-3.0+
|
||||
- GPL-3.0-only
|
||||
- GPL-3.0-or-later
|
||||
- GPL-3.0-with-autoconf-exception
|
||||
- GPL-3.0-with-GCC-exception
|
||||
- gSOAP-1.3b
|
||||
- HaskellReport
|
||||
- Hippocratic-2.1
|
||||
- HPND
|
||||
- HPND-sell-variant
|
||||
- HTMLTIDY
|
||||
- IBM-pibs
|
||||
- ICU
|
||||
- IJG
|
||||
- ImageMagick
|
||||
- iMatix
|
||||
- Imlib2
|
||||
- Info-ZIP
|
||||
- Intel
|
||||
- Intel-ACPI
|
||||
- Interbase-1.0
|
||||
- IPA
|
||||
- IPL-1.0
|
||||
- ISC
|
||||
- Jam
|
||||
- JasPer-2.0
|
||||
- JPNIC
|
||||
- JSON
|
||||
- Knuth-CTAN
|
||||
- LAL-1.2
|
||||
- LAL-1.3
|
||||
- Latex2e
|
||||
- Leptonica
|
||||
- LGPL-2.0
|
||||
- LGPL-2.0+
|
||||
- LGPL-2.0-only
|
||||
- LGPL-2.0-or-later
|
||||
- LGPL-2.1
|
||||
- LGPL-2.1+
|
||||
- LGPL-2.1-only
|
||||
- LGPL-2.1-or-later
|
||||
- LGPL-3.0
|
||||
- LGPL-3.0+
|
||||
- LGPL-3.0-only
|
||||
- LGPL-3.0-or-later
|
||||
- LGPLLR
|
||||
- Libpng
|
||||
- libpng-2.0
|
||||
- libselinux-1.0
|
||||
- libtiff
|
||||
- libutil-David-Nugent
|
||||
- LiLiQ-P-1.1
|
||||
- LiLiQ-R-1.1
|
||||
- LiLiQ-Rplus-1.1
|
||||
- Linux-man-pages-copyleft
|
||||
- Linux-OpenIB
|
||||
- LOOP
|
||||
- LPL-1.0
|
||||
- LPL-1.02
|
||||
- LPPL-1.0
|
||||
- LPPL-1.1
|
||||
- LPPL-1.2
|
||||
- LPPL-1.3a
|
||||
- LPPL-1.3c
|
||||
- LZMA-SDK-9.11-to-9.20
|
||||
- LZMA-SDK-9.22
|
||||
- MakeIndex
|
||||
- Minpack
|
||||
- MirOS
|
||||
- MIT
|
||||
- MIT-0
|
||||
- MIT-advertising
|
||||
- MIT-CMU
|
||||
- MIT-enna
|
||||
- MIT-feh
|
||||
- MIT-Modern-Variant
|
||||
- MIT-open-group
|
||||
- MITNFA
|
||||
- Motosoto
|
||||
- mpi-permissive
|
||||
- mpich2
|
||||
- MPL-1.0
|
||||
- MPL-1.1
|
||||
- MPL-2.0
|
||||
- MPL-2.0-no-copyleft-exception
|
||||
- mplus
|
||||
- MS-LPL
|
||||
- MS-PL
|
||||
- MS-RL
|
||||
- MTLL
|
||||
- MulanPSL-1.0
|
||||
- MulanPSL-2.0
|
||||
- Multics
|
||||
- Mup
|
||||
- NAIST-2003
|
||||
- NASA-1.3
|
||||
- Naumen
|
||||
- NBPL-1.0
|
||||
- NCGL-UK-2.0
|
||||
- NCSA
|
||||
- Net-SNMP
|
||||
- NetCDF
|
||||
- Newsletr
|
||||
- NGPL
|
||||
- NICTA-1.0
|
||||
- NIST-PD
|
||||
- NIST-PD-fallback
|
||||
- NLOD-1.0
|
||||
- NLOD-2.0
|
||||
- NLPL
|
||||
- Nokia
|
||||
- NOSL
|
||||
- Noweb
|
||||
- NPL-1.0
|
||||
- NPL-1.1
|
||||
- NPOSL-3.0
|
||||
- NRL
|
||||
- NTP
|
||||
- NTP-0
|
||||
- Nunit
|
||||
- O-UDA-1.0
|
||||
- OCCT-PL
|
||||
- OCLC-2.0
|
||||
- ODbL-1.0
|
||||
- ODC-By-1.0
|
||||
- OFL-1.0
|
||||
- OFL-1.0-no-RFN
|
||||
- OFL-1.0-RFN
|
||||
- OFL-1.1
|
||||
- OFL-1.1-no-RFN
|
||||
- OFL-1.1-RFN
|
||||
- OGC-1.0
|
||||
- OGDL-Taiwan-1.0
|
||||
- OGL-Canada-2.0
|
||||
- OGL-UK-1.0
|
||||
- OGL-UK-2.0
|
||||
- OGL-UK-3.0
|
||||
- OGTSL
|
||||
- OLDAP-1.1
|
||||
- OLDAP-1.2
|
||||
- OLDAP-1.3
|
||||
- OLDAP-1.4
|
||||
- OLDAP-2.0
|
||||
- OLDAP-2.0.1
|
||||
- OLDAP-2.1
|
||||
- OLDAP-2.2
|
||||
- OLDAP-2.2.1
|
||||
- OLDAP-2.2.2
|
||||
- OLDAP-2.3
|
||||
- OLDAP-2.4
|
||||
- OLDAP-2.5
|
||||
- OLDAP-2.6
|
||||
- OLDAP-2.7
|
||||
- OLDAP-2.8
|
||||
- OML
|
||||
- OpenSSL
|
||||
- OPL-1.0
|
||||
- OPUBL-1.0
|
||||
- OSET-PL-2.1
|
||||
- OSL-1.0
|
||||
- OSL-1.1
|
||||
- OSL-2.0
|
||||
- OSL-2.1
|
||||
- OSL-3.0
|
||||
- Parity-6.0.0
|
||||
- Parity-7.0.0
|
||||
- PDDL-1.0
|
||||
- PHP-3.0
|
||||
- PHP-3.01
|
||||
- Plexus
|
||||
- PolyForm-Noncommercial-1.0.0
|
||||
- PolyForm-Small-Business-1.0.0
|
||||
- PostgreSQL
|
||||
- PSF-2.0
|
||||
- psfrag
|
||||
- psutils
|
||||
- Python-2.0
|
||||
- Python-2.0.1
|
||||
- Qhull
|
||||
- QPL-1.0
|
||||
- Rdisc
|
||||
- RHeCos-1.1
|
||||
- RPL-1.1
|
||||
- RPL-1.5
|
||||
- RPSL-1.0
|
||||
- RSA-MD
|
||||
- RSCPL
|
||||
- Ruby
|
||||
- SAX-PD
|
||||
- Saxpath
|
||||
- SCEA
|
||||
- SchemeReport
|
||||
- Sendmail
|
||||
- Sendmail-8.23
|
||||
- SGI-B-1.0
|
||||
- SGI-B-1.1
|
||||
- SGI-B-2.0
|
||||
- SHL-0.5
|
||||
- SHL-0.51
|
||||
- SimPL-2.0
|
||||
- SISSL
|
||||
- SISSL-1.2
|
||||
- Sleepycat
|
||||
- SMLNJ
|
||||
- SMPPL
|
||||
- SNIA
|
||||
- Spencer-86
|
||||
- Spencer-94
|
||||
- Spencer-99
|
||||
- SPL-1.0
|
||||
- SSH-OpenSSH
|
||||
- SSH-short
|
||||
- SSPL-1.0
|
||||
- StandardML-NJ
|
||||
- SugarCRM-1.1.3
|
||||
- SWL
|
||||
- Symlinks
|
||||
- TAPR-OHL-1.0
|
||||
- TCL
|
||||
- TCP-wrappers
|
||||
- TMate
|
||||
- TORQUE-1.1
|
||||
- TOSL
|
||||
- TU-Berlin-1.0
|
||||
- TU-Berlin-2.0
|
||||
- UCL-1.0
|
||||
- Unicode-DFS-2015
|
||||
- Unicode-DFS-2016
|
||||
- Unicode-TOU
|
||||
- Unlicense
|
||||
- UPL-1.0
|
||||
- Vim
|
||||
- VOSTROM
|
||||
- VSL-1.0
|
||||
- W3C
|
||||
- W3C-19980720
|
||||
- W3C-20150513
|
||||
- Watcom-1.0
|
||||
- Wsuipa
|
||||
- WTFPL
|
||||
- wxWindows
|
||||
- X11
|
||||
- X11-distribute-modifications-variant
|
||||
- Xerox
|
||||
- XFree86-1.1
|
||||
- xinetd
|
||||
- Xnet
|
||||
- xpp
|
||||
- XSkat
|
||||
- YPL-1.0
|
||||
- YPL-1.1
|
||||
- Zed
|
||||
- Zend-2.0
|
||||
- Zimbra-1.3
|
||||
- Zimbra-1.4
|
||||
- Zlib
|
||||
- zlib-acknowledgement
|
||||
- ZPL-1.1
|
||||
- ZPL-2.0
|
||||
- ZPL-2.1
|
||||
ScorecardContentV2Version:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
commit:
|
||||
type: string
|
||||
ScorecardContentV2Repository:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
commit:
|
||||
type: string
|
||||
description: Commit SHA where the scorecard checks where executed
|
||||
ScorecardV2Check:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
enum:
|
||||
- CII-Best-Practices
|
||||
- Fuzzing
|
||||
- Pinned-Dependencies
|
||||
- CI-Tests
|
||||
- Maintained
|
||||
- Packaging
|
||||
- SAST
|
||||
- Dependency-Update-Tool
|
||||
- Token-Permissions
|
||||
- Security-Policy
|
||||
- Signed-Releases
|
||||
- Binary-Artifacts
|
||||
- Branch-Protection
|
||||
- Code-Review
|
||||
- Contributors
|
||||
- Vulnerabilities
|
||||
- Dangerous-Workflow
|
||||
- License
|
||||
- Webhooks
|
||||
score:
|
||||
type: number
|
||||
reason:
|
||||
type: string
|
||||
details:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
ScorecardContentV2:
|
||||
type: object
|
||||
properties:
|
||||
date:
|
||||
type: string
|
||||
example: '2010-01-01'
|
||||
format: date
|
||||
repository:
|
||||
$ref: '#/components/schemas/ScorecardContentV2Repository'
|
||||
scorecard:
|
||||
$ref: '#/components/schemas/ScorecardContentV2Version'
|
||||
score:
|
||||
type: number
|
||||
checks:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ScorecardV2Check'
|
||||
Scorecard:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
enum:
|
||||
- V2
|
||||
content:
|
||||
$ref: '#/components/schemas/ScorecardContentV2'
|
||||
PackageDependency:
|
||||
type: object
|
||||
properties:
|
||||
package_version:
|
||||
$ref: '#/components/schemas/PackageVersion'
|
||||
description:
|
||||
type: string
|
||||
licenses:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/License'
|
||||
distance:
|
||||
type: integer
|
||||
PackageDependents:
|
||||
type: object
|
||||
properties:
|
||||
total_dependents:
|
||||
type: integer
|
||||
direct_dependents:
|
||||
type: integer
|
||||
indirect_dependents:
|
||||
type: integer
|
||||
PackageProjectInfo:
|
||||
type: object
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
display_name:
|
||||
type: string
|
||||
issues:
|
||||
type: integer
|
||||
forks:
|
||||
type: integer
|
||||
stars:
|
||||
type: integer
|
||||
link:
|
||||
type: string
|
||||
PackageVersionInsight:
|
||||
type: object
|
||||
properties:
|
||||
package_version:
|
||||
$ref: '#/components/schemas/PackageVersion'
|
||||
projects:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PackageProjectInfo'
|
||||
licenses:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/License'
|
||||
dependents:
|
||||
$ref: '#/components/schemas/PackageDependents'
|
||||
dependencies:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PackageDependency'
|
||||
scorecard:
|
||||
$ref: '#/components/schemas/Scorecard'
|
||||
22
auth.go
22
auth.go
@ -3,8 +3,12 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/term"
|
||||
|
||||
"github.com/safedep/vet/internal/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -13,7 +17,8 @@ var (
|
||||
|
||||
func newAuthCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "auth",
|
||||
Use: "auth",
|
||||
Short: "Configure and verify Insights API authentication",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("You must choose an appropriate command: configure, verify\n")
|
||||
os.Exit(1)
|
||||
@ -31,7 +36,20 @@ func configureAuthCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "configure",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Run auth.Configure()
|
||||
fmt.Print("Enter API Key: ")
|
||||
key, err := term.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = auth.Configure(auth.Config{
|
||||
ApiUrl: authInsightApiBaseUrl,
|
||||
ApiKey: string(key),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
return nil
|
||||
},
|
||||
|
||||
14
go.mod
14
go.mod
@ -3,12 +3,24 @@ module github.com/safedep/vet
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/deepmap/oapi-codegen v1.12.4
|
||||
github.com/google/osv-scanner v1.0.2
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
golang.org/x/term v0.3.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
32
go.sum
32
go.sum
@ -1,9 +1,22 @@
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s=
|
||||
github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
|
||||
github.com/google/osv-scanner v1.0.2 h1:EiDbP8XQhEvo9I7WZMvA7OkJinyOULhNTD7SITS2tBY=
|
||||
github.com/google/osv-scanner v1.0.2/go.mod h1:KTYFW64rATMvw7MtWAVXxIkG7u0R86n6VUKM8pzOzF0=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@ -13,12 +26,27 @@ github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
97
internal/auth/auth.go
Normal file
97
internal/auth/auth.go
Normal file
@ -0,0 +1,97 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
apiUrlEnvKey = "VET_INSIGHTS_API_URL"
|
||||
apiKeyEnvKey = "VET_INSIGHTS_API_KEY"
|
||||
|
||||
defaultApiUrl = "https://api.safedep.io/insights/v1"
|
||||
|
||||
homeRelativeConfigPath = ".safedep/vet-auth.yml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ApiUrl string `yaml:"api_url"`
|
||||
ApiKey string `yaml:"api_key"`
|
||||
}
|
||||
|
||||
// Global config to be used during runtime
|
||||
var globalConfig *Config
|
||||
|
||||
func init() {
|
||||
loadConfiguration()
|
||||
}
|
||||
|
||||
func Configure(m Config) error {
|
||||
globalConfig = &m
|
||||
return persistConfiguration()
|
||||
}
|
||||
|
||||
func Verify() error {
|
||||
// TODO: Verify by actually calling insight API
|
||||
return nil
|
||||
}
|
||||
|
||||
func ApiUrl() string {
|
||||
if url, ok := os.LookupEnv(apiUrlEnvKey); ok {
|
||||
return url
|
||||
}
|
||||
|
||||
if globalConfig != nil {
|
||||
return globalConfig.ApiUrl
|
||||
}
|
||||
|
||||
return defaultApiUrl
|
||||
}
|
||||
|
||||
func ApiKey() string {
|
||||
if key, ok := os.LookupEnv(apiKeyEnvKey); ok {
|
||||
return key
|
||||
}
|
||||
|
||||
if globalConfig != nil {
|
||||
return globalConfig.ApiKey
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func loadConfiguration() error {
|
||||
path, err := os.UserHomeDir()
|
||||
path = filepath.Join(path, homeRelativeConfigPath)
|
||||
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var config Config
|
||||
err = yaml.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config deserialization failed: %w", err)
|
||||
}
|
||||
|
||||
globalConfig = &config
|
||||
return nil
|
||||
}
|
||||
|
||||
func persistConfiguration() error {
|
||||
data, err := yaml.Marshal(globalConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config serialization failed: %w", err)
|
||||
}
|
||||
|
||||
path, err := os.UserHomeDir()
|
||||
path = filepath.Join(path, homeRelativeConfigPath)
|
||||
|
||||
os.MkdirAll(filepath.Dir(path), os.ModePerm)
|
||||
return ioutil.WriteFile(path, data, 0600)
|
||||
}
|
||||
5
main.go
5
main.go
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -33,6 +34,10 @@ func main() {
|
||||
cmd.AddCommand(newScanCommand())
|
||||
cmd.AddCommand(newVersionCommand())
|
||||
|
||||
cobra.OnInitialize(func() {
|
||||
logger.SetLogLevel(verbose, debug)
|
||||
})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
23
pkg/analyzer/analyzer.go
Normal file
23
pkg/analyzer/analyzer.go
Normal file
@ -0,0 +1,23 @@
|
||||
package analyzer
|
||||
|
||||
import "github.com/safedep/vet/pkg/models"
|
||||
|
||||
type AnalyzerEvent struct {
|
||||
// Analyzer generating this event
|
||||
Source string
|
||||
|
||||
// Entities on which event was generated
|
||||
Manifest *models.PackageManifest
|
||||
Package *models.Package
|
||||
}
|
||||
|
||||
// Callback to receive events from analyzer
|
||||
type AnalyzerEventHandler func(event *AnalyzerEvent) error
|
||||
|
||||
// Contract for an analyzer
|
||||
type Analyzer interface {
|
||||
Name() string
|
||||
|
||||
Analyze(manifest *models.PackageManifest,
|
||||
handler AnalyzerEventHandler) error
|
||||
}
|
||||
58
pkg/analyzer/json_dump.go
Normal file
58
pkg/analyzer/json_dump.go
Normal file
@ -0,0 +1,58 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/models"
|
||||
)
|
||||
|
||||
type jsonDumperAnalyzer struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
func NewJsonDumperAnalyzer(dir string) (Analyzer, error) {
|
||||
fi, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create dir: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("cannot stat dir: %w", err)
|
||||
}
|
||||
|
||||
if !fi.IsDir() {
|
||||
return nil, fmt.Errorf("%s is not a dir", dir)
|
||||
}
|
||||
|
||||
return &jsonDumperAnalyzer{dir: dir}, nil
|
||||
}
|
||||
|
||||
func (j *jsonDumperAnalyzer) Name() string {
|
||||
return "JSON Dump Generator"
|
||||
}
|
||||
|
||||
func (j *jsonDumperAnalyzer) Analyze(manifest *models.PackageManifest,
|
||||
handler AnalyzerEventHandler) error {
|
||||
|
||||
logger.Infof("Running analyzer: %s", j.Name())
|
||||
data, err := json.MarshalIndent(manifest, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to JSON serialize manifest: %w", err)
|
||||
}
|
||||
|
||||
path := filepath.Join(j.dir, fmt.Sprintf("%s-%s--%d-dump.json",
|
||||
manifest.Ecosystem,
|
||||
filepath.Base(manifest.Path),
|
||||
rand.Intn(2<<15)))
|
||||
|
||||
return ioutil.WriteFile(path, data, 0600)
|
||||
}
|
||||
@ -8,7 +8,7 @@ import (
|
||||
|
||||
func init() {
|
||||
logrus.SetOutput(os.Stdout)
|
||||
logrus.SetLevel(logrus.InfoLevel)
|
||||
logrus.SetLevel(logrus.WarnLevel)
|
||||
}
|
||||
|
||||
func SetLogLevel(verbose, debug bool) {
|
||||
@ -18,7 +18,6 @@ func SetLogLevel(verbose, debug bool) {
|
||||
|
||||
if debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetReportCaller(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
72
pkg/common/utils/workq.go
Normal file
72
pkg/common/utils/workq.go
Normal file
@ -0,0 +1,72 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
)
|
||||
|
||||
type WorkQueueItem interface {
|
||||
Id() string
|
||||
}
|
||||
|
||||
type WorkQueueFn[T WorkQueueItem] func(q *WorkQueue[T], item T) error
|
||||
|
||||
type WorkQueue[T WorkQueueItem] struct {
|
||||
done chan bool
|
||||
m sync.Mutex
|
||||
concurrency int
|
||||
wg sync.WaitGroup
|
||||
handler WorkQueueFn[T]
|
||||
status sync.Map
|
||||
items chan T
|
||||
}
|
||||
|
||||
func NewWorkQueue[T WorkQueueItem](bufferSize int, concurrency int,
|
||||
handler WorkQueueFn[T]) *WorkQueue[T] {
|
||||
return &WorkQueue[T]{
|
||||
handler: handler,
|
||||
concurrency: concurrency,
|
||||
items: make(chan T, bufferSize),
|
||||
done: make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *WorkQueue[T]) Start() {
|
||||
for i := 0; i < q.concurrency; i++ {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-q.done:
|
||||
return
|
||||
case item := <-q.items:
|
||||
err := q.handler(q, item)
|
||||
if err != nil {
|
||||
logger.Errorf("Handler fn failed with %v", err)
|
||||
}
|
||||
|
||||
q.wg.Done()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (q *WorkQueue[T]) Wait() {
|
||||
q.wg.Wait()
|
||||
}
|
||||
|
||||
func (q *WorkQueue[T]) Stop() {
|
||||
close(q.done)
|
||||
}
|
||||
|
||||
func (q *WorkQueue[T]) Add(item T) {
|
||||
if _, ok := q.status.Load(item.Id()); ok {
|
||||
return
|
||||
}
|
||||
|
||||
q.status.Store(item.Id(), true)
|
||||
q.wg.Add(1)
|
||||
|
||||
q.items <- item
|
||||
}
|
||||
73
pkg/models/models.go
Normal file
73
pkg/models/models.go
Normal file
@ -0,0 +1,73 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/osv-scanner/pkg/lockfile"
|
||||
"github.com/safedep/vet/gen/insightapi"
|
||||
)
|
||||
|
||||
const (
|
||||
EcosystemMaven = "Maven"
|
||||
EcosystemRubyGems = "RubyGems"
|
||||
EcosystemGo = "Go"
|
||||
EcosystemNpm = "npm"
|
||||
EcosystemPyPI = "PyPI"
|
||||
EcosystemCargo = "Cargo"
|
||||
EcosystemNuGet = "NuGet"
|
||||
EcosystemPackagist = "Packagist"
|
||||
EcosystemHex = "Hex"
|
||||
EcosystemPub = "Pub"
|
||||
)
|
||||
|
||||
// Represents a package manifest that contains a list
|
||||
// of packages. Example: pom.xml, requirements.txt
|
||||
type PackageManifest struct {
|
||||
// Filesystem path of this manifest
|
||||
Path string `json:"path"`
|
||||
|
||||
// Ecosystem to interpret this manifest
|
||||
Ecosystem string `json:"ecosystem"`
|
||||
|
||||
// List of packages obtained by parsing the manifest
|
||||
Packages []*Package `json:"packages"`
|
||||
}
|
||||
|
||||
// Represents a package such as a version of a library defined as a dependency
|
||||
// in Gemfile.lock, pom.xml etc.
|
||||
type Package struct {
|
||||
lockfile.PackageDetails `json:"package_detail"`
|
||||
|
||||
// Insights obtained for this package
|
||||
Insights *insightapi.PackageVersionInsight `json:"insights"`
|
||||
|
||||
// This package is a transitive dependency of parent package
|
||||
Parent *Package `json:"-"`
|
||||
|
||||
// Depth of this package in dependency tree
|
||||
Depth int `json:"depth"`
|
||||
|
||||
// Manifest from where this package was found directly or indirectly
|
||||
Manifest *PackageManifest `json:"-"`
|
||||
}
|
||||
|
||||
func (p *Package) Id() string {
|
||||
h := fnv.New64a()
|
||||
h.Write([]byte(fmt.Sprintf("%s/%s/%s",
|
||||
strings.ToLower(p.Manifest.Ecosystem),
|
||||
strings.ToLower(p.PackageDetails.Name),
|
||||
strings.ToLower(p.PackageDetails.Version))))
|
||||
|
||||
return strconv.FormatUint(h.Sum64(), 16)
|
||||
}
|
||||
|
||||
func NewPackageDetail(e, n, v string) lockfile.PackageDetails {
|
||||
return lockfile.PackageDetails{
|
||||
Ecosystem: lockfile.Ecosystem(e),
|
||||
Name: n,
|
||||
Version: v,
|
||||
}
|
||||
}
|
||||
89
pkg/parser/parser.go
Normal file
89
pkg/parser/parser.go
Normal file
@ -0,0 +1,89 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/osv-scanner/pkg/lockfile"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/models"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
Ecosystem() string
|
||||
Parse(lockfilePath string) (models.PackageManifest, error)
|
||||
}
|
||||
|
||||
type parserWrapper struct {
|
||||
parser lockfile.PackageDetailsParser
|
||||
parseAs string
|
||||
}
|
||||
|
||||
func List() []string {
|
||||
return lockfile.ListParsers()
|
||||
}
|
||||
|
||||
func FindParser(lockfilePath, lockfileAs string) (Parser, error) {
|
||||
p, pa := lockfile.FindParser(lockfilePath, lockfileAs)
|
||||
if p != nil {
|
||||
return &parserWrapper{parser: p, parseAs: pa}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no parser found with: %s for: %s", lockfileAs,
|
||||
lockfilePath)
|
||||
}
|
||||
|
||||
func (pw *parserWrapper) Ecosystem() string {
|
||||
switch pw.parseAs {
|
||||
case "Cargo.lock":
|
||||
return models.EcosystemCargo
|
||||
case "composer.lock":
|
||||
return models.EcosystemPackagist
|
||||
case "Gemfile.lock":
|
||||
return models.EcosystemRubyGems
|
||||
case "go.mod":
|
||||
return models.EcosystemGo
|
||||
case "mix.lock":
|
||||
return models.EcosystemHex
|
||||
case "package-lock.json":
|
||||
return models.EcosystemNpm
|
||||
case "pnpm-lock.yaml":
|
||||
return models.EcosystemNpm
|
||||
case "poetry.lock":
|
||||
return models.EcosystemPyPI
|
||||
case "pom.xml":
|
||||
return models.EcosystemMaven
|
||||
case "pubspec.lock":
|
||||
return models.EcosystemPub
|
||||
case "requirements.txt":
|
||||
return models.EcosystemPyPI
|
||||
case "yarn.lock":
|
||||
return models.EcosystemNpm
|
||||
case "gradle.lockfile":
|
||||
return models.EcosystemMaven
|
||||
case "buildscript-gradle.lockfile":
|
||||
return models.EcosystemMaven
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (pw *parserWrapper) Parse(lockfilePath string) (models.PackageManifest, error) {
|
||||
pm := models.PackageManifest{Path: lockfilePath,
|
||||
Ecosystem: pw.Ecosystem()}
|
||||
|
||||
logger.Infof("[%s] Parsing %s", pw.parseAs, lockfilePath)
|
||||
|
||||
packages, err := pw.parser(lockfilePath)
|
||||
if err != nil {
|
||||
return pm, err
|
||||
}
|
||||
|
||||
for _, pkg := range packages {
|
||||
pm.Packages = append(pm.Packages, &models.Package{
|
||||
PackageDetails: pkg,
|
||||
Manifest: &pm,
|
||||
})
|
||||
}
|
||||
|
||||
return pm, nil
|
||||
}
|
||||
26
pkg/parser/parser_test.go
Normal file
26
pkg/parser/parser_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestListParser(t *testing.T) {
|
||||
parsers := List()
|
||||
assert.Equal(t, 14, len(parsers))
|
||||
}
|
||||
|
||||
func TestInvalidEcosystemMapping(t *testing.T) {
|
||||
pw := &parserWrapper{parseAs: "nothing"}
|
||||
assert.Empty(t, pw.Ecosystem())
|
||||
}
|
||||
|
||||
func TestEcosystemMapping(t *testing.T) {
|
||||
for _, lf := range List() {
|
||||
t.Run(lf, func(t *testing.T) {
|
||||
pw := &parserWrapper{parseAs: lf}
|
||||
assert.NotEmpty(t, pw.Ecosystem())
|
||||
})
|
||||
}
|
||||
}
|
||||
94
pkg/scanner/enrich.go
Normal file
94
pkg/scanner/enrich.go
Normal file
@ -0,0 +1,94 @@
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/safedep/vet/gen/insightapi"
|
||||
"github.com/safedep/vet/internal/auth"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/models"
|
||||
)
|
||||
|
||||
// Callback to receive a discovery package dependency
|
||||
type PackageDependencyCallbackFn func(pkg *models.Package) error
|
||||
|
||||
// Enrich meta information associated with
|
||||
// the package
|
||||
type PackageMetaEnricher interface {
|
||||
Name() string
|
||||
Enrich(pkg *models.Package, cb PackageDependencyCallbackFn) error
|
||||
}
|
||||
|
||||
type insightsBasedPackageEnricher struct {
|
||||
client *insightapi.ClientWithResponses
|
||||
}
|
||||
|
||||
func NewInsightBasedPackageEnricher() PackageMetaEnricher {
|
||||
apiKeyApplier := func(ctx context.Context, req *http.Request) error {
|
||||
req.Header.Set("Authorization", auth.ApiKey())
|
||||
return nil
|
||||
}
|
||||
|
||||
client, err := insightapi.NewClientWithResponses(auth.ApiUrl(),
|
||||
insightapi.WithRequestEditorFn(apiKeyApplier))
|
||||
if err != nil {
|
||||
// TODO: Handle
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &insightsBasedPackageEnricher{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *insightsBasedPackageEnricher) Name() string {
|
||||
return "Insights API"
|
||||
}
|
||||
|
||||
func (e *insightsBasedPackageEnricher) Enrich(pkg *models.Package,
|
||||
cb PackageDependencyCallbackFn) error {
|
||||
|
||||
logger.Infof("[%s] Enriching %s/%s", pkg.Manifest.Ecosystem,
|
||||
pkg.PackageDetails.Name, pkg.PackageDetails.Version)
|
||||
|
||||
res, err := e.client.GetPackageVersionInsightWithResponse(context.Background(),
|
||||
pkg.Manifest.Ecosystem, pkg.Name, pkg.Version)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to enrich package: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if res.HTTPResponse.StatusCode != 200 {
|
||||
return fmt.Errorf("bad response: %d: %s", res.HTTPResponse.StatusCode,
|
||||
res.HTTPResponse.Status)
|
||||
}
|
||||
|
||||
if (res.JSON200 == nil) || (res.JSON200.Dependencies == nil) {
|
||||
return fmt.Errorf("unexpected nil response from Insight API")
|
||||
}
|
||||
|
||||
for _, dep := range *res.JSON200.Dependencies {
|
||||
if strings.EqualFold(dep.PackageVersion.Name, pkg.PackageDetails.Name) {
|
||||
// Skip self references in dependency
|
||||
continue
|
||||
}
|
||||
|
||||
err := cb(&models.Package{
|
||||
Parent: pkg,
|
||||
Depth: pkg.Depth + 1,
|
||||
Manifest: pkg.Manifest,
|
||||
PackageDetails: models.NewPackageDetail(dep.PackageVersion.Ecosystem,
|
||||
dep.PackageVersion.Name, dep.PackageVersion.Version),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to invoke package dependency callback: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
pkg.Insights = res.JSON200
|
||||
return nil
|
||||
}
|
||||
146
pkg/scanner/scanner.go
Normal file
146
pkg/scanner/scanner.go
Normal file
@ -0,0 +1,146 @@
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"github.com/safedep/vet/pkg/analyzer"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/common/utils"
|
||||
"github.com/safedep/vet/pkg/models"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ConcurrentAnalyzer int
|
||||
TransitiveAnalysis bool
|
||||
TransitiveDepth int
|
||||
}
|
||||
|
||||
type packageManifestScanner struct {
|
||||
config Config
|
||||
enrichers []PackageMetaEnricher
|
||||
analyzers []analyzer.Analyzer
|
||||
}
|
||||
|
||||
func NewPackageManifestScanner(config Config,
|
||||
enrichers []PackageMetaEnricher,
|
||||
analyzers []analyzer.Analyzer) *packageManifestScanner {
|
||||
return &packageManifestScanner{
|
||||
config: config,
|
||||
enrichers: enrichers,
|
||||
analyzers: analyzers,
|
||||
}
|
||||
}
|
||||
|
||||
// Autodiscover lockfiles
|
||||
func (s *packageManifestScanner) ScanDirectory(dir string) error {
|
||||
logger.Infof("Starting package manifest scanner on dir: %s", dir)
|
||||
|
||||
manifests, err := scanDirectoryForManifests(dir)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to scan directory: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("Discovered %d manifest(s)", len(manifests))
|
||||
return s.analyzeManifests(manifests)
|
||||
}
|
||||
|
||||
// Scan specific lockfiles, optionally interpreted as instead of
|
||||
// automatic parser selection
|
||||
func (s *packageManifestScanner) ScanLockfiles(lockfiles []string,
|
||||
lockfileAs string) error {
|
||||
logger.Infof("Scannding %d lockfiles as %s", len(lockfiles), lockfileAs)
|
||||
|
||||
manifests, err := scanLockfilesForManifests(lockfiles, lockfileAs)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to scan lockfiles: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("Discovered %d manifest(s)", len(manifests))
|
||||
return s.analyzeManifests(manifests)
|
||||
}
|
||||
|
||||
func (s *packageManifestScanner) analyzeManifests(manifests []*models.PackageManifest) error {
|
||||
for _, manifest := range manifests {
|
||||
logger.Infof("Analysing %s as %s ecosystem with %d packages", manifest.Path,
|
||||
manifest.Ecosystem, len(manifest.Packages))
|
||||
|
||||
err := s.enrichManifest(manifest)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to enrich %s manifest %s : %v",
|
||||
manifest.Ecosystem, manifest.Path, err)
|
||||
}
|
||||
|
||||
err = s.analyzeManifest(manifest)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to analyze %s manifest %v : %v",
|
||||
manifest.Ecosystem, manifest.Path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *packageManifestScanner) analyzeManifest(manifest *models.PackageManifest) error {
|
||||
for _, task := range s.analyzers {
|
||||
err := task.Analyze(manifest, func(event *analyzer.AnalyzerEvent) error {
|
||||
// Handle analyzer event
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Errorf("Analyzer %s failed: %v", task.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *packageManifestScanner) enrichManifest(manifest *models.PackageManifest) error {
|
||||
// FIXME: Potential deadlock situation in case of channel buffer is full
|
||||
// because the goroutines perform both read and write to channel. Write occurs
|
||||
// when goroutine invokes the work queue handler and the handler pushes back
|
||||
// the dependencies
|
||||
q := utils.NewWorkQueue[*models.Package](100000,
|
||||
s.config.ConcurrentAnalyzer,
|
||||
s.packageEnrichWorkQueueHandler())
|
||||
q.Start()
|
||||
|
||||
for _, pkg := range manifest.Packages {
|
||||
q.Add(pkg)
|
||||
}
|
||||
|
||||
q.Wait()
|
||||
q.Stop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *packageManifestScanner) packageEnrichWorkQueueHandler() utils.WorkQueueFn[*models.Package] {
|
||||
return func(q *utils.WorkQueue[*models.Package], item *models.Package) error {
|
||||
for _, enricher := range s.enrichers {
|
||||
err := enricher.Enrich(item, s.packageDependencyHandler(q))
|
||||
if err != nil {
|
||||
logger.Errorf("Enricher %s failed with %v", enricher.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *packageManifestScanner) packageDependencyHandler(q *utils.WorkQueue[*models.Package]) PackageDependencyCallbackFn {
|
||||
return func(pkg *models.Package) error {
|
||||
if !s.config.TransitiveAnalysis {
|
||||
return nil
|
||||
}
|
||||
|
||||
if pkg.Depth >= s.config.TransitiveDepth {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Debugf("Adding transitive dependency %s/%v to work queue",
|
||||
pkg.PackageDetails.Name, pkg.PackageDetails.Version)
|
||||
q.Add(pkg)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
64
pkg/scanner/utils.go
Normal file
64
pkg/scanner/utils.go
Normal file
@ -0,0 +1,64 @@
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/models"
|
||||
"github.com/safedep/vet/pkg/parser"
|
||||
)
|
||||
|
||||
func scanDirectoryForManifests(dir string) ([]*models.PackageManifest, error) {
|
||||
var manifests []*models.PackageManifest
|
||||
err := filepath.WalkDir(dir, func(path string, info os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() && info.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := parser.FindParser(path, "")
|
||||
if err == nil {
|
||||
// We have a parseable file
|
||||
manifest, err := p.Parse(path)
|
||||
if err != nil {
|
||||
logger.Warnf("Failed to parse: %s due to %v", path, err)
|
||||
} else {
|
||||
manifests = append(manifests, &manifest)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return manifests, err
|
||||
}
|
||||
|
||||
func scanLockfilesForManifests(lockfiles []string, lockfileAs string) ([]*models.PackageManifest, error) {
|
||||
var manifests []*models.PackageManifest
|
||||
for _, lf := range lockfiles {
|
||||
p, err := parser.FindParser(lf, lockfileAs)
|
||||
if err != nil {
|
||||
logger.Warnf("Failed to parse %s as %s", lf, lockfileAs)
|
||||
continue
|
||||
}
|
||||
|
||||
manifest, err := p.Parse(lf)
|
||||
if err != nil {
|
||||
logger.Warnf("Failed to parse: %s due to %v", lf, err)
|
||||
continue
|
||||
}
|
||||
|
||||
manifests = append(manifests, &manifest)
|
||||
}
|
||||
|
||||
return manifests, nil
|
||||
}
|
||||
89
scan.go
89
scan.go
@ -1,21 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/safedep/vet/pkg/analyzer"
|
||||
"github.com/safedep/vet/pkg/common/logger"
|
||||
"github.com/safedep/vet/pkg/parser"
|
||||
"github.com/safedep/vet/pkg/scanner"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
lockfiles []string
|
||||
lockfileAs string
|
||||
baseDirectory string
|
||||
lockfiles []string
|
||||
lockfileAs string
|
||||
baseDirectory string
|
||||
transitiveAnalysis bool
|
||||
transitiveDepth int
|
||||
concurrency int
|
||||
dumpJsonManifest bool
|
||||
dumpJsonManifestDir string
|
||||
)
|
||||
|
||||
func newScanCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "scan",
|
||||
Use: "scan",
|
||||
Short: "Scan and analyse package manifests",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
startScan()
|
||||
return nil
|
||||
@ -31,13 +41,74 @@ func newScanCommand() *cobra.Command {
|
||||
"The directory to scan for lockfiles")
|
||||
cmd.Flags().StringArrayVarP(&lockfiles, "lockfiles", "L", []string{},
|
||||
"List of lockfiles to scan")
|
||||
cmd.Flags().StringVarP(&baseDirectory, "lockfile-as", "", "",
|
||||
"Ecosystem to interpret the lockfile as")
|
||||
cmd.Flags().StringVarP(&lockfileAs, "lockfile-as", "", "",
|
||||
"Parser to use for the lockfile (vet scan parsers to list)")
|
||||
cmd.Flags().BoolVarP(&transitiveAnalysis, "transitive", "", true,
|
||||
"Analyze transitive dependencies")
|
||||
cmd.Flags().IntVarP(&transitiveDepth, "transitive-depth", "", 2,
|
||||
"Analyze transitive dependencies till depth")
|
||||
cmd.Flags().IntVarP(&concurrency, "concurrency", "C", 10,
|
||||
"Number of goroutines to use for analysis")
|
||||
cmd.Flags().BoolVarP(&dumpJsonManifest, "json-dump", "", false,
|
||||
"Dump enriched manifests as JSON docs")
|
||||
cmd.Flags().StringVarP(&dumpJsonManifestDir, "json-dump-dir", "", "",
|
||||
"Dump dir for enriched JSON docs")
|
||||
|
||||
cmd.AddCommand(listParsersCommand())
|
||||
return cmd
|
||||
}
|
||||
|
||||
func startScan() {
|
||||
logger.SetLogLevel(verbose, debug)
|
||||
logger.Infof("Starting vet scanner")
|
||||
func listParsersCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "parsers",
|
||||
Short: "List available lockfile parsers",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Available Lockfile Parsers\n")
|
||||
fmt.Printf("==========================\n\n")
|
||||
|
||||
for idx, p := range parser.List() {
|
||||
fmt.Printf("[%d] %s\n", idx, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func startScan() {
|
||||
err := internalStartScan()
|
||||
if err != nil {
|
||||
logger.Errorf("Scan completed with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func internalStartScan() error {
|
||||
analyzers := []analyzer.Analyzer{}
|
||||
if dumpJsonManifest {
|
||||
task, err := analyzer.NewJsonDumperAnalyzer(dumpJsonManifestDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
analyzers = append(analyzers, task)
|
||||
}
|
||||
|
||||
enrichers := []scanner.PackageMetaEnricher{
|
||||
scanner.NewInsightBasedPackageEnricher(),
|
||||
}
|
||||
|
||||
pmScanner := scanner.NewPackageManifestScanner(scanner.Config{
|
||||
TransitiveAnalysis: transitiveAnalysis,
|
||||
TransitiveDepth: transitiveDepth,
|
||||
ConcurrentAnalyzer: concurrency,
|
||||
}, enrichers, analyzers)
|
||||
|
||||
var err error
|
||||
if len(lockfiles) > 0 {
|
||||
err = pmScanner.ScanLockfiles(lockfiles, lockfileAs)
|
||||
} else {
|
||||
err = pmScanner.ScanDirectory(baseDirectory)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -12,7 +12,8 @@ var VERSION string
|
||||
|
||||
func newVersionCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Use: "version",
|
||||
Short: "Show version and build information",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Fprintf(os.Stdout, "Version: %s\n", VERSION)
|
||||
fmt.Fprintf(os.Stdout, "CommitSHA: %s\n", GITCOMMIT)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user