diff --git a/Makefile b/Makefile index 82b0f39d29d..cbb754def99 100644 --- a/Makefile +++ b/Makefile @@ -140,6 +140,19 @@ fixtures: $(call title,Generating test fixtures) cd syft/cataloger/java/test-fixtures/java-builds && make +.PHONY: generate-json-schema +generate-json-schema: clean-json-schema-examples integration ## Generate a new json schema for the json presenter, derived from integration test cases + docker run \ + -i \ + --rm \ + -v $(shell pwd)/json-schema:/work \ + -w /work \ + python:3.8 \ + bash -x -c "\ + pip install -r requirements.txt && \ + python generate.py \ + " + .PHONY: clear-test-cache clear-test-cache: ## Delete all test cache (built docker image tars) find . -type f -wholename "**/test-fixtures/tar-cache/*.tar" -delete @@ -215,7 +228,7 @@ release: clean-dist ## Build and publish final binaries and packages .github/scripts/update-version-file.sh "$(DISTDIR)" "$(VERSION)" .PHONY: clean -clean: clean-dist clean-snapshot ## Remove previous builds and result reports +clean: clean-dist clean-snapshot clean-json-schema-examples ## Remove previous builds and result reports rm -rf $(RESULTSDIR)/* .PHONY: clean-snapshot @@ -225,3 +238,7 @@ clean-snapshot: .PHONY: clean-dist clean-dist: rm -rf $(DISTDIR) $(TEMPDIR)/goreleaser.yaml + +.PHONY: clean-json-schema-examples +clean-json-schema-examples: + rm json-schema/examples/* \ No newline at end of file diff --git a/go.mod b/go.mod index 951428f40e5..20a8baba408 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/wagoodman/go-rpmdb v0.0.0-20200719223757-ce54a4b0607b github.com/wagoodman/jotframe v0.0.0-20200730190914-3517092dd163 github.com/x-cray/logrus-prefixed-formatter v0.5.2 + github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 golang.org/x/sys v0.0.0-20200610111108-226ff32320da // indirect google.golang.org/genproto v0.0.0-20200615140333-fd031eab31e7 // indirect diff --git a/go.sum b/go.sum index 9639dfc1be0..6041ad86099 100644 --- a/go.sum +++ b/go.sum @@ -841,6 +841,12 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7V github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= github.com/xanzy/go-gitlab v0.32.0 h1:tBm+OXv1t+KBsqlXkSDFz+YUjRM0GFsjpOWYOod3Ebs= github.com/xanzy/go-gitlab v0.32.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= diff --git a/json-schema/.gitignore b/json-schema/.gitignore new file mode 100644 index 00000000000..56bef8cb359 --- /dev/null +++ b/json-schema/.gitignore @@ -0,0 +1 @@ +examples/ \ No newline at end of file diff --git a/json-schema/generate.py b/json-schema/generate.py new file mode 100644 index 00000000000..d7f8ad0b3ec --- /dev/null +++ b/json-schema/generate.py @@ -0,0 +1,30 @@ +#!/usr/env/bin python3 +import os +import glob +import json + +from genson import SchemaBuilder + +EXAMPLES_DIR = "examples/" +OUTPUT = "schema.json" + + +def main(): + builder = SchemaBuilder() + + print("Generating new Syft json schema...") + for filepath in glob.glob(os.path.join(EXAMPLES_DIR, '*.json')): + with open(filepath, 'r') as f: + print(f" adding {filepath}") + builder.add_object(json.loads(f.read())) + + print("Building schema...") + new_schema = builder.to_schema() + with open(OUTPUT, 'w') as f: + f.write(json.dumps(new_schema, sort_keys=True, indent=4)) + + print(f"New schema written to '{OUTPUT}'") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/json-schema/requirements.txt b/json-schema/requirements.txt new file mode 100644 index 00000000000..85d0dbaaa4c --- /dev/null +++ b/json-schema/requirements.txt @@ -0,0 +1 @@ +genson \ No newline at end of file diff --git a/json-schema/schema.json b/json-schema/schema.json new file mode 100644 index 00000000000..52899ed6ffc --- /dev/null +++ b/json-schema/schema.json @@ -0,0 +1,523 @@ +{ + "$schema": "http://json-schema.org/schema#", + "properties": { + "artifacts": { + "items": { + "properties": { + "metadata": { + "properties": { + "architecture": { + "type": "string" + }, + "description": { + "type": "string" + }, + "epoch": { + "type": "integer" + }, + "files": { + "items": { + "properties": { + "checksum": { + "type": "string" + }, + "owner-gid": { + "type": "string" + }, + "owner-uid": { + "type": "string" + }, + "path": { + "type": "string" + }, + "permissions": { + "type": "string" + } + }, + "required": [ + "checksum", + "owner-gid", + "owner-uid", + "path", + "permissions" + ], + "type": "object" + }, + "type": "array" + }, + "git-commit-of-apk-port": { + "type": "string" + }, + "installed-size": { + "type": "integer" + }, + "license": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "manifest": { + "anyOf": [ + { + "type": "null" + }, + { + "properties": { + "extra-fields": { + "properties": { + "Archiver-Version": { + "type": "string" + }, + "Build-Jdk": { + "type": "string" + }, + "Built-By": { + "type": "string" + }, + "Created-By": { + "type": "string" + }, + "Extension-Name": { + "type": "string" + }, + "Group-Id": { + "type": "string" + }, + "Hudson-Version": { + "type": "string" + }, + "Jenkins-Version": { + "type": "string" + }, + "Long-Name": { + "type": "string" + }, + "Main-Class": { + "type": "string" + }, + "Minimum-Java-Version": { + "type": "string" + }, + "Plugin-Dependencies": { + "type": "string" + }, + "Plugin-Developers": { + "type": "string" + }, + "Plugin-License-Name": { + "type": "string" + }, + "Plugin-License-Url": { + "type": "string" + }, + "Plugin-ScmUrl": { + "type": "string" + }, + "Plugin-Version": { + "type": "string" + }, + "Short-Name": { + "type": "string" + } + }, + "required": [ + "Archiver-Version", + "Build-Jdk", + "Built-By", + "Created-By" + ], + "type": "object" + }, + "implementation-title": { + "type": "string" + }, + "implementation-vendor": { + "type": "string" + }, + "implementation-version": { + "type": "string" + }, + "manifest-version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "specification-title": { + "type": "string" + }, + "specification-vendor": { + "type": "string" + }, + "specification-version": { + "type": "string" + } + }, + "required": [ + "extra-fields", + "implementation-title", + "implementation-vendor", + "implementation-version", + "manifest-version", + "name", + "specification-title", + "specification-vendor", + "specification-version" + ], + "type": "object" + } + ] + }, + "origin-package": { + "type": "string" + }, + "package": { + "type": "string" + }, + "parent-package": { + "anyOf": [ + { + "type": "null" + }, + { + "properties": { + "found-by": { + "type": "string" + }, + "language": { + "type": "integer" + }, + "licenses": { + "type": "null" + }, + "manifest": { + "type": "string" + }, + "metadata": { + "properties": { + "manifest": { + "properties": { + "extra-fields": { + "properties": { + "Archiver-Version": { + "type": "string" + }, + "Build-Jdk": { + "type": "string" + }, + "Built-By": { + "type": "string" + }, + "Created-By": { + "type": "string" + }, + "Extension-Name": { + "type": "string" + }, + "Group-Id": { + "type": "string" + }, + "Hudson-Version": { + "type": "string" + }, + "Jenkins-Version": { + "type": "string" + }, + "Long-Name": { + "type": "string" + }, + "Main-Class": { + "type": "string" + }, + "Minimum-Java-Version": { + "type": "string" + }, + "Plugin-Dependencies": { + "type": "string" + }, + "Plugin-Developers": { + "type": "string" + }, + "Plugin-License-Name": { + "type": "string" + }, + "Plugin-License-Url": { + "type": "string" + }, + "Plugin-ScmUrl": { + "type": "string" + }, + "Plugin-Version": { + "type": "string" + }, + "Short-Name": { + "type": "string" + } + }, + "required": [ + "Archiver-Version", + "Build-Jdk", + "Built-By", + "Created-By" + ], + "type": "object" + }, + "implementation-title": { + "type": "string" + }, + "implementation-vendor": { + "type": "string" + }, + "implementation-version": { + "type": "string" + }, + "manifest-version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "specification-title": { + "type": "string" + }, + "specification-vendor": { + "type": "string" + }, + "specification-version": { + "type": "string" + } + }, + "required": [ + "extra-fields", + "implementation-title", + "implementation-vendor", + "implementation-version", + "manifest-version", + "name", + "specification-title", + "specification-vendor", + "specification-version" + ], + "type": "object" + }, + "parent-package": { + "type": "null" + }, + "pom-properties": { + "properties": { + "Path": { + "type": "string" + }, + "artifact-id": { + "type": "string" + }, + "extra-fields": { + "type": "null" + }, + "group-id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "Path", + "artifact-id", + "extra-fields", + "group-id", + "name", + "version" + ], + "type": "object" + } + }, + "required": [ + "manifest", + "parent-package", + "pom-properties" + ], + "type": "object" + }, + "sources": { + "type": "null" + }, + "type": { + "type": "integer" + }, + "version": { + "type": "string" + } + }, + "required": [ + "found-by", + "language", + "licenses", + "manifest", + "metadata", + "sources", + "type", + "version" + ], + "type": "object" + } + ] + }, + "pom-properties": { + "properties": { + "Path": { + "type": "string" + }, + "artifact-id": { + "type": "string" + }, + "extra-fields": { + "type": "null" + }, + "group-id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "Path", + "artifact-id", + "extra-fields", + "group-id", + "name", + "version" + ], + "type": "object" + }, + "pull-checksum": { + "type": "string" + }, + "pull-dependencies": { + "type": "string" + }, + "release": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "source": { + "type": "string" + }, + "url": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + }, + "name": { + "type": "string" + }, + "sources": { + "items": { + "properties": { + "found-by": { + "type": "string" + }, + "locations": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "found-by", + "locations" + ], + "type": "object" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "name", + "sources", + "type", + "version" + ], + "type": "object" + }, + "type": "array" + }, + "directory": { + "type": "string" + }, + "image": { + "properties": { + "digest": { + "type": "string" + }, + "layers": { + "items": { + "properties": { + "digest": { + "type": "string" + }, + "media-type": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "required": [ + "digest", + "media-type", + "size" + ], + "type": "object" + }, + "type": "array" + }, + "media-type": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "tags": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "digest", + "layers", + "media-type", + "size", + "tags" + ], + "type": "object" + } + }, + "required": [ + "artifacts" + ], + "type": "object" +} \ No newline at end of file diff --git a/syft/cataloger/apkdb/parse_apk_db.go b/syft/cataloger/apkdb/parse_apk_db.go index 39903b53637..98591f9f0df 100644 --- a/syft/cataloger/apkdb/parse_apk_db.go +++ b/syft/cataloger/apkdb/parse_apk_db.go @@ -4,9 +4,11 @@ import ( "bufio" "fmt" "io" + "path" "strconv" "strings" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" "github.com/mitchellh/mapstructure" ) @@ -52,10 +54,14 @@ func parseApkDB(_ string, reader io.Reader) ([]pkg.Package, error) { return packages, nil } +// nolint:funlen func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) { var entry pkg.ApkMetadata pkgFields := make(map[string]interface{}) - files := make([]string, 0) + files := make([]pkg.ApkFileRecord, 0) + + var fileRecord *pkg.ApkFileRecord + lastFile := "/" scanner := bufio.NewScanner(reader) for scanner.Scan() { @@ -70,9 +76,33 @@ func parseApkDBEntry(reader io.Reader) (*pkg.ApkMetadata, error) { switch key { case "F": - // extract all file entries, don't store in map - files = append(files, value) + lastFile = "/" + value continue + case "R": + newFileRecord := pkg.ApkFileRecord{ + Path: path.Join(lastFile, value), + } + files = append(files, newFileRecord) + fileRecord = &files[len(files)-1] + case "a": + ownershipFields := strings.Split(value, ":") + if len(ownershipFields) != 3 { + log.Errorf("unexpected APK ownership field: %q", value) + continue + } + if fileRecord == nil { + log.Errorf("ownership field with no parent record: %q", value) + continue + } + fileRecord.OwnerUID = ownershipFields[0] + fileRecord.OwnerGUI = ownershipFields[1] + fileRecord.Permissions = ownershipFields[2] + case "Z": + if fileRecord == nil { + log.Errorf("checksum field with no parent record: %q", value) + continue + } + fileRecord.Checksum = value case "I", "S": // coerce to integer iVal, err := strconv.Atoi(value) diff --git a/syft/cataloger/apkdb/parse_apk_db_test.go b/syft/cataloger/apkdb/parse_apk_db_test.go index 1023737d625..18068e1ee45 100644 --- a/syft/cataloger/apkdb/parse_apk_db_test.go +++ b/syft/cataloger/apkdb/parse_apk_db_test.go @@ -31,7 +31,43 @@ func TestSinglePackage(t *testing.T) { PullDependencies: "scanelf so:libc.musl-x86_64.so.1", PullChecksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", GitCommitOfAport: "4024cc3b29ad4c65544ad068b8f59172b5494306", - Files: []string{"sbin", "usr", "usr/bin"}, + Files: []pkg.ApkFileRecord{ + { + Path: "/sbin/ldconfig", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", + }, + { + Path: "/usr/bin/iconv", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", + }, + { + Path: "/usr/bin/ldd", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", + }, + { + Path: "/usr/bin/getconf", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", + }, + { + Path: "/usr/bin/getent", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", + }, + }, }, }, } @@ -92,7 +128,7 @@ func TestMultiplePackages(t *testing.T) { PullChecksum: "Q1p78yvTLG094tHE1+dToJGbmYzQE=", GitCommitOfAport: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479", PullDependencies: "musl-utils", - Files: []string{}, + Files: []pkg.ApkFileRecord{}, }, }, { @@ -114,7 +150,43 @@ func TestMultiplePackages(t *testing.T) { PullDependencies: "scanelf so:libc.musl-x86_64.so.1", PullChecksum: "Q1bTtF5526tETKfL+lnigzIDvm+2o=", GitCommitOfAport: "4024cc3b29ad4c65544ad068b8f59172b5494306", - Files: []string{"sbin", "usr", "usr/bin"}, + Files: []pkg.ApkFileRecord{ + { + Path: "/sbin/ldconfig", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1Kja2+POZKxEkUOZqwSjC6kmaED4=", + }, + { + Path: "/usr/bin/iconv", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1CVmFbdY+Hv6/jAHl1gec2Kbx1EY=", + }, + { + Path: "/usr/bin/ldd", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1yFAhGggmL7ERgbIA7KQxyTzf3ks=", + }, + { + Path: "/usr/bin/getconf", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1dAdYK8M/INibRQF5B3Rw7cmNDDA=", + }, + { + Path: "/usr/bin/getent", + OwnerUID: "0", + OwnerGUI: "0", + Permissions: "755", + Checksum: "Q1eR2Dz/WylabgbWMTkd2+hGmEya4=", + }, + }, }, }, }, diff --git a/syft/pkg/metadata.go b/syft/pkg/metadata.go index d886aa06a47..150bbb4908e 100644 --- a/syft/pkg/metadata.go +++ b/syft/pkg/metadata.go @@ -1,61 +1,70 @@ package pkg -// TODO: consider keeping the remaining values as an embedded map // Available fields are described at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html // in the --showformat section type DpkgMetadata struct { - Package string `mapstructure:"Package"` - Source string `mapstructure:"Source"` - Version string `mapstructure:"Version"` + Package string `mapstructure:"Package" json:"package"` + Source string `mapstructure:"Source" json:"source"` + Version string `mapstructure:"Version" json:"version"` + // TODO: consider keeping the remaining values as an embedded map } type RpmMetadata struct { - Epoch int `mapstructure:"Epoch"` - Arch string `mapstructure:"Arch"` - Release string `mapstructure:"Release"` + Epoch int `mapstructure:"Epoch" json:"epoch"` + Arch string `mapstructure:"Arch" json:"architecture"` + Release string `mapstructure:"Release" json:"release"` + // TODO: consider keeping the remaining values as an embedded map } type JavaManifest struct { - Name string `mapstructure:"Name"` - ManifestVersion string `mapstructure:"Manifest-Version"` - SpecTitle string `mapstructure:"Specification-Title"` - SpecVersion string `mapstructure:"Specification-Version"` - SpecVendor string `mapstructure:"Specification-Vendor"` - ImplTitle string `mapstructure:"Implementation-Title"` - ImplVersion string `mapstructure:"Implementation-Version"` - ImplVendor string `mapstructure:"Implementation-Vendor"` - Extra map[string]string `mapstructure:",remain"` + Name string `mapstructure:"Name" json:"name"` + ManifestVersion string `mapstructure:"Manifest-Version" json:"manifest-version"` + SpecTitle string `mapstructure:"Specification-Title" json:"specification-title"` + SpecVersion string `mapstructure:"Specification-Version" json:"specification-version"` + SpecVendor string `mapstructure:"Specification-Vendor" json:"specification-vendor"` + ImplTitle string `mapstructure:"Implementation-Title" json:"implementation-title"` + ImplVersion string `mapstructure:"Implementation-Version" json:"implementation-version"` + ImplVendor string `mapstructure:"Implementation-Vendor" json:"implementation-vendor"` + Extra map[string]string `mapstructure:",remain" json:"extra-fields"` } type PomProperties struct { Path string - Name string `mapstructure:"name"` - GroupID string `mapstructure:"groupId"` - ArtifactID string `mapstructure:"artifactId"` - Version string `mapstructure:"version"` - Extra map[string]string `mapstructure:",remain"` + Name string `mapstructure:"name" json:"name"` + GroupID string `mapstructure:"groupId" json:"group-id"` + ArtifactID string `mapstructure:"artifactId" json:"artifact-id"` + Version string `mapstructure:"version" json:"version"` + Extra map[string]string `mapstructure:",remain" json:"extra-fields"` } type JavaMetadata struct { - Manifest *JavaManifest `mapstructure:"Manifest"` - PomProperties *PomProperties `mapstructure:"PomProperties"` - Parent *Package + Manifest *JavaManifest `mapstructure:"Manifest" json:"manifest"` + PomProperties *PomProperties `mapstructure:"PomProperties" json:"pom-properties"` + Parent *Package `json:"parent-package"` } // source: https://wiki.alpinelinux.org/wiki/Apk_spec type ApkMetadata struct { - Package string `mapstructure:"P"` - OriginPackage string `mapstructure:"o"` - Maintainer string `mapstructure:"m"` - Version string `mapstructure:"V"` - License string `mapstructure:"L"` - Architecture string `mapstructure:"A"` - URL string `mapstructure:"U"` - Description string `mapstructure:"T"` - Size int `mapstructure:"S"` - InstalledSize int `mapstructure:"I"` - PullDependencies string `mapstructure:"D"` - PullChecksum string `mapstructure:"C"` - GitCommitOfAport string `mapstructure:"c"` - Files []string + Package string `mapstructure:"P" json:"package"` + OriginPackage string `mapstructure:"o" json:"origin-package"` + Maintainer string `mapstructure:"m" json:"maintainer"` + Version string `mapstructure:"V" json:"version"` + License string `mapstructure:"L" json:"license"` + Architecture string `mapstructure:"A" json:"architecture"` + URL string `mapstructure:"U" json:"url"` + Description string `mapstructure:"T" json:"description"` + Size int `mapstructure:"S" json:"size"` + InstalledSize int `mapstructure:"I" json:"installed-size"` + PullDependencies string `mapstructure:"D" json:"pull-dependencies"` + PullChecksum string `mapstructure:"C" json:"pull-checksum"` + GitCommitOfAport string `mapstructure:"c" json:"git-commit-of-apk-port"` + Files []ApkFileRecord `json:"files"` +} + +type ApkFileRecord struct { + Path string `json:"path"` + OwnerUID string `json:"owner-uid"` + OwnerGUI string `json:"owner-gid"` + Permissions string `json:"permissions"` + Checksum string `json:"checksum"` } diff --git a/syft/pkg/package.go b/syft/pkg/package.go index a19b78eaf93..1964956ea76 100644 --- a/syft/pkg/package.go +++ b/syft/pkg/package.go @@ -12,15 +12,15 @@ type ID int64 // Package represents an application or library that has been bundled into a distributable format type Package struct { - id ID // this is set when a package is added to the catalog - Name string - Version string - FoundBy string - Source []file.Reference - Licenses []string - Language Language // TODO: should this support multiple languages as a slice? - Type Type - Metadata interface{} + id ID // this is set when a package is added to the catalog + Name string `json:"manifest"` + Version string `json:"version"` + FoundBy string `json:"found-by"` + Source []file.Reference `json:"sources"` + Licenses []string `json:"licenses"` // TODO: should we move this into metadata? + Language Language `json:"language"` // TODO: should this support multiple languages as a slice? + Type Type `json:"type"` + Metadata interface{} `json:"metadata,omitempty"` } func (p Package) ID() ID { diff --git a/syft/presenter/json/presenter.go b/syft/presenter/json/presenter.go index 3bff0c1475a..9f327670a53 100644 --- a/syft/presenter/json/presenter.go +++ b/syft/presenter/json/presenter.go @@ -5,7 +5,6 @@ import ( "fmt" "io" - "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/scope" ) @@ -24,36 +23,35 @@ func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter { type document struct { Artifacts []artifact `json:"artifacts"` - Image image `json:"image"` - Source string + Image *image `json:"image,omitempty"` + Directory *string `json:"directory,omitempty"` } type image struct { Layers []layer `json:"layers"` Size int64 `json:"size"` Digest string `json:"digest"` - MediaType string `json:"mediaType"` + MediaType string `json:"media-type"` Tags []string `json:"tags"` } type layer struct { - MediaType string `json:"mediaType"` + MediaType string `json:"media-type"` Digest string `json:"digest"` Size int64 `json:"size"` } type source struct { - FoundBy string `json:"foundBy"` - Effects []string `json:"effects"` + FoundBy string `json:"found-by"` + Locations []string `json:"locations"` } type artifact struct { - Name string `json:"name"` - Version string `json:"version"` - Type string `json:"type"` - Cataloger string `json:"cataloger"` - Sources []source `json:"sources"` - Metadata interface{} `json:"metadata,omitempty"` + Name string `json:"name"` + Version string `json:"version"` + Type string `json:"type"` + Sources []source `json:"sources"` + Metadata interface{} `json:"metadata,omitempty"` } func (pres *Presenter) Present(output io.Writer) error { @@ -69,15 +67,25 @@ func (pres *Presenter) Present(output io.Writer) error { for idx, tag := range src.Img.Metadata.Tags { tags[idx] = tag.String() } - doc.Image = image{ + doc.Image = &image{ Digest: src.Img.Metadata.Digest, Size: src.Img.Metadata.Size, MediaType: string(src.Img.Metadata.MediaType), Tags: tags, Layers: make([]layer, len(src.Img.Layers)), } + + // populate image metadata + for idx, l := range src.Img.Layers { + doc.Image.Layers[idx] = layer{ + MediaType: string(l.Metadata.MediaType), + Digest: l.Metadata.Digest, + Size: l.Metadata.Size, + } + } + case scope.DirSource: - doc.Source = pres.scope.DirSrc.Path + doc.Directory = &pres.scope.DirSrc.Path default: return fmt.Errorf("unsupported source: %T", src) } @@ -93,8 +101,8 @@ func (pres *Presenter) Present(output io.Writer) error { for idx := range p.Source { srcObj := source{ - FoundBy: p.FoundBy, - Effects: []string{}, // TODO + FoundBy: p.FoundBy, + Locations: []string{string(p.Source[idx].Path)}, } art.Sources[idx] = srcObj } @@ -102,11 +110,9 @@ func (pres *Presenter) Present(output io.Writer) error { doc.Artifacts = append(doc.Artifacts, art) } - bytes, err := json.Marshal(&doc) - if err != nil { - log.Errorf("failed to marshal json (presenter=json): %w", err) - } - - _, err = output.Write(bytes) - return err + enc := json.NewEncoder(output) + // prevent > and < from being escaped in the payload + enc.SetEscapeHTML(false) + enc.SetIndent("", " ") + return enc.Encode(&doc) } diff --git a/syft/presenter/json/presenter_test.go b/syft/presenter/json/presenter_test.go index aa6ce6d664c..23c805ef00c 100644 --- a/syft/presenter/json/presenter_test.go +++ b/syft/presenter/json/presenter_test.go @@ -109,7 +109,4 @@ func TestJsonImgsPresenter(t *testing.T) { diffs := dmp.DiffMain(string(actual), string(expected), true) t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs)) } - - // TODO: add me back in when there is a JSON schema - // validateAgainstV1Schema(t, string(actual)) } diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index 28bb0eb2414..d7ebd76f8f1 100644 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/syft/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -1 +1,17 @@ -{"artifacts":[{"name":"package-1","version":"1.0.1","type":"deb","cataloger":"","sources":[]},{"name":"package-2","version":"2.0.1","type":"deb","cataloger":"","sources":[]}],"image":{"layers":null,"size":0,"digest":"","mediaType":"","tags":null},"Source":"/some/path"} \ No newline at end of file +{ + "artifacts": [ + { + "name": "package-1", + "version": "1.0.1", + "type": "deb", + "sources": [] + }, + { + "name": "package-2", + "version": "2.0.1", + "type": "deb", + "sources": [] + } + ], + "directory": "/some/path" +} diff --git a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index 76fd2d5b6b5..279f2a8d3ac 100644 --- a/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/syft/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -1 +1,55 @@ -{"artifacts":[{"name":"package-1","version":"1.0.1","type":"deb","cataloger":"","sources":[{"foundBy":"","effects":[]}]},{"name":"package-2","version":"2.0.1","type":"deb","cataloger":"","sources":[{"foundBy":"","effects":[]}]}],"image":{"layers":[{"mediaType":"","digest":"","size":0},{"mediaType":"","digest":"","size":0},{"mediaType":"","digest":"","size":0}],"size":65,"digest":"sha256:3c53d2d891940f8d8e95acb77b58752f54dc5de9d91d19dd90ced2db76256cea","mediaType":"application/vnd.docker.distribution.manifest.v2+json","tags":["anchore-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7"]},"Source":""} \ No newline at end of file +{ + "artifacts": [ + { + "name": "package-1", + "version": "1.0.1", + "type": "deb", + "sources": [ + { + "found-by": "", + "locations": [ + "/somefile-1.txt" + ] + } + ] + }, + { + "name": "package-2", + "version": "2.0.1", + "type": "deb", + "sources": [ + { + "found-by": "", + "locations": [ + "/somefile-2.txt" + ] + } + ] + } + ], + "image": { + "layers": [ + { + "media-type": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:056c0789fa9ad629ceae6d09713fb035f84115af3c4a88a43aa60f13bc683053", + "size": 22 + }, + { + "media-type": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:b461c48116592c570a66fed71d5b09662a8172e168b7938cf317af47872cdc9b", + "size": 16 + }, + { + "media-type": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "digest": "sha256:00b80053e05c01da485015610d288ce3185fac00d251e2ada02b45a7a7c5f589", + "size": 27 + } + ], + "size": 65, + "digest": "sha256:3c53d2d891940f8d8e95acb77b58752f54dc5de9d91d19dd90ced2db76256cea", + "media-type": "application/vnd.docker.distribution.manifest.v2+json", + "tags": [ + "anchore-fixture-image-simple:04e16e44161c8888a1a963720fd0443cbf7eef8101434c431de8725cd98cc9f7" + ] + } +} diff --git a/syft/presenter/table/presenter_test.go b/syft/presenter/table/presenter_test.go index 1a2d201cd67..40684af7b85 100644 --- a/syft/presenter/table/presenter_test.go +++ b/syft/presenter/table/presenter_test.go @@ -62,7 +62,4 @@ func TestTablePresenter(t *testing.T) { diffs := dmp.DiffMain(string(actual), string(expected), true) t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs)) } - - // TODO: add me back in when there is a JSON schema - // validateAgainstV1Schema(t, string(actual)) } diff --git a/syft/presenter/text/presenter_test.go b/syft/presenter/text/presenter_test.go index 84ee62ccbbc..9df07730e0d 100644 --- a/syft/presenter/text/presenter_test.go +++ b/syft/presenter/text/presenter_test.go @@ -12,7 +12,7 @@ import ( "github.com/sergi/go-diff/diffmatchpatch" ) -var update = flag.Bool("update", false, "update the *.golden files for json presenters") +var update = flag.Bool("update", false, "update the *.golden files for text presenters") func TestTextDirPresenter(t *testing.T) { var buffer bytes.Buffer diff --git a/test/integration/fixture_image_distro_test.go b/test/integration/distro_test.go similarity index 100% rename from test/integration/fixture_image_distro_test.go rename to test/integration/distro_test.go diff --git a/test/integration/json_schema_test.go b/test/integration/json_schema_test.go new file mode 100644 index 00000000000..a1ecc74dae7 --- /dev/null +++ b/test/integration/json_schema_test.go @@ -0,0 +1,121 @@ +// +build integration + +package integration + +import ( + "bytes" + "fmt" + "github.com/anchore/go-testutils" + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/presenter" + "github.com/anchore/syft/syft/scope" + "github.com/xeipuuv/gojsonschema" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "testing" +) + +const jsonSchemaExamplesPath = "json-schema/examples" + +func repoRoot(t *testing.T) string { + t.Helper() + repoRoot, err := exec.Command("git", "rev-parse", "--show-toplevel").Output() + if err != nil { + t.Fatalf("unable to find repo root dir: %+v", err) + } + absRepoRoot, err := filepath.Abs(strings.TrimSpace(string(repoRoot))) + if err != nil { + t.Fatal("unable to get abs path to repo root:", err) + } + return absRepoRoot +} + +func validateAgainstV1Schema(t *testing.T, json string) { + fullSchemaPath := path.Join(repoRoot(t), "json-schema", "schema.json") + schemaLoader := gojsonschema.NewReferenceLoader(fmt.Sprintf("file://%s", fullSchemaPath)) + documentLoader := gojsonschema.NewStringLoader(json) + + result, err := gojsonschema.Validate(schemaLoader, documentLoader) + if err != nil { + t.Fatal("unable to validate json schema:", err.Error()) + } + + if !result.Valid() { + t.Errorf("failed json schema validation:") + for _, desc := range result.Errors() { + t.Errorf(" - %s\n", desc) + } + } +} + +func testJsonSchema(t *testing.T, catalog *pkg.Catalog, theScope *scope.Scope, prefix string) { + // make the json output example dir if it does not exist + absJsonSchemaExamplesPath := path.Join(repoRoot(t), jsonSchemaExamplesPath) + if _, err := os.Stat(absJsonSchemaExamplesPath); os.IsNotExist(err) { + os.Mkdir(absJsonSchemaExamplesPath, 0755) + } + + output := bytes.NewBufferString("") + + p := presenter.GetPresenter(presenter.JSONPresenter, *theScope, catalog) + if p == nil { + t.Fatal("unable to get presenter") + } + + err := p.Present(output) + if err != nil { + t.Fatalf("unable to present: %+v", err) + } + + // we use the examples dir as a way to use integration tests to drive what valid examples are in case we + // want to update the json schema. We do not want to validate the output of the presentation format as the + // contents may change regularly, making the integration tests brittle. + testFileName := prefix + "_" + path.Base(t.Name()) + ".json" + testFilePath := path.Join(absJsonSchemaExamplesPath, testFileName) + + fh, err := os.OpenFile(testFilePath, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + t.Fatalf("unable to open json example path: %+v", err) + } + _, err = fh.WriteString(output.String()) + if err != nil { + t.Fatalf("unable to write json example: %+v", err) + } + + validateAgainstV1Schema(t, output.String()) +} + +func TestJsonSchemaImg(t *testing.T) { + fixtureImageName := "image-pkg-coverage" + _, cleanup := testutils.GetFixtureImage(t, "docker-archive", fixtureImageName) + tarPath := testutils.GetFixtureImageTarPath(t, fixtureImageName) + defer cleanup() + + catalog, theScope, _, err := syft.Catalog("docker-archive://"+tarPath, scope.AllLayersScope) + if err != nil { + t.Fatalf("failed to catalog image: %+v", err) + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + testJsonSchema(t, catalog, theScope, "img") + }) + } +} + +func TestJsonSchemaDirs(t *testing.T) { + catalog, theScope, _, err := syft.Catalog("dir://test-fixtures/image-pkg-coverage", scope.AllLayersScope) + if err != nil { + t.Errorf("unable to create scope from dir: %+v", err) + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + testJsonSchema(t, catalog, theScope, "dir") + }) + } +} diff --git a/test/integration/pkg_cases.go b/test/integration/pkg_cases.go new file mode 100644 index 00000000000..0ea5adc4c4c --- /dev/null +++ b/test/integration/pkg_cases.go @@ -0,0 +1,161 @@ +// +build integration + +package integration + +import "github.com/anchore/syft/syft/pkg" + +var cases = []struct { + name string + pkgType pkg.Type + pkgLanguage pkg.Language + pkgInfo map[string]string +}{ + { + name: "find rpmdb packages", + pkgType: pkg.RpmPkg, + pkgInfo: map[string]string{ + "dive": "0.9.2", + }, + }, + { + name: "find dpkg packages", + pkgType: pkg.DebPkg, + pkgInfo: map[string]string{ + "apt": "1.8.2", + }, + }, + { + name: "find java packages", + pkgType: pkg.JavaPkg, + pkgLanguage: pkg.Java, + pkgInfo: map[string]string{ + "example-java-app-maven": "0.1.0", + "example-jenkins-plugin": "1.0-SNAPSHOT", // the jenkins HPI file has a nested JAR of the same name + }, + }, + { + name: "find jenkins plugins", + pkgType: pkg.JenkinsPluginPkg, + pkgLanguage: pkg.Java, + pkgInfo: map[string]string{ + "example-jenkins-plugin": "1.0-SNAPSHOT", + }, + }, + { + name: "find python wheel packages", + pkgType: pkg.WheelPkg, + pkgLanguage: pkg.Python, + pkgInfo: map[string]string{ + "Pygments": "2.6.1", + "requests": "2.10.0", + }, + }, + { + name: "find javascript npm packages", + pkgType: pkg.NpmPkg, + pkgLanguage: pkg.JavaScript, + pkgInfo: map[string]string{ + "get-stdin": "8.0.0", + }, + }, + { + name: "find javascript yarn packages", + pkgType: pkg.YarnPkg, + pkgLanguage: pkg.JavaScript, + pkgInfo: map[string]string{ + "@babel/code-frame": "7.10.4", + }, + }, + { + name: "find python egg packages", + pkgType: pkg.EggPkg, + pkgLanguage: pkg.Python, + pkgInfo: map[string]string{ + "requests": "2.22.0", + "otherpkg": "2.19.0", + }, + }, + { + name: "find python packages", + pkgType: pkg.PythonRequirementsPkg, + pkgLanguage: pkg.Python, + pkgInfo: map[string]string{ + "flask": "4.0.0", + }, + }, + { + name: "find bundler packages", + pkgType: pkg.BundlerPkg, + pkgLanguage: pkg.Ruby, + pkgInfo: map[string]string{ + "actionmailer": "4.1.1", + "actionpack": "4.1.1", + "actionview": "4.1.1", + "activemodel": "4.1.1", + "activerecord": "4.1.1", + "activesupport": "4.1.1", + "arel": "5.0.1.20140414130214", + "bootstrap-sass": "3.1.1.1", + "builder": "3.2.2", + "coffee-rails": "4.0.1", + "coffee-script": "2.2.0", + "coffee-script-source": "1.7.0", + "erubis": "2.7.0", + "execjs": "2.0.2", + "hike": "1.2.3", + "i18n": "0.6.9", + "jbuilder": "2.0.7", + "jquery-rails": "3.1.0", + "json": "1.8.1", + "kgio": "2.9.2", + "libv8": "3.16.14.3", + "mail": "2.5.4", + "mime-types": "1.25.1", + "minitest": "5.3.4", + "multi_json": "1.10.1", + "mysql2": "0.3.16", + "polyglot": "0.3.4", + "rack": "1.5.2", + "rack-test": "0.6.2", + "rails": "4.1.1", + "railties": "4.1.1", + "raindrops": "0.13.0", + "rake": "10.3.2", + "rdoc": "4.1.1", + "ref": "1.0.5", + "sass": "3.2.19", + "sass-rails": "4.0.3", + "sdoc": "0.4.0", + "spring": "1.1.3", + "sprockets": "2.11.0", + "sprockets-rails": "2.1.3", + "sqlite3": "1.3.9", + "therubyracer": "0.12.1", + "thor": "0.19.1", + "thread_safe": "0.3.3", + "tilt": "1.4.1", + "treetop": "1.4.15", + "turbolinks": "2.2.2", + "tzinfo": "1.2.0", + "uglifier": "2.5.0", + "unicorn": "4.8.3", + }, + }, + { + + name: "find apkdb packages", + pkgType: pkg.ApkPkg, + pkgInfo: map[string]string{ + "musl-utils": "1.1.24-r2", + "libc-utils": "0.7.2-r0", + }, + }, + { + name: "find golang modules", + pkgType: pkg.GoModulePkg, + pkgLanguage: pkg.Go, + pkgInfo: map[string]string{ + "github.com/bmatcuk/doublestar": "v1.3.1", + }, + }, +} diff --git a/test/integration/fixture_pkg_coverage_test.go b/test/integration/pkg_coverage_test.go similarity index 52% rename from test/integration/fixture_pkg_coverage_test.go rename to test/integration/pkg_coverage_test.go index 4a59d4e0df8..5b3a081971a 100644 --- a/test/integration/fixture_pkg_coverage_test.go +++ b/test/integration/pkg_coverage_test.go @@ -14,162 +14,6 @@ import ( "github.com/anchore/syft/syft/scope" ) -var cases = []struct { - name string - pkgType pkg.Type - pkgLanguage pkg.Language - pkgInfo map[string]string -}{ - { - name: "find rpmdb packages", - pkgType: pkg.RpmPkg, - pkgInfo: map[string]string{ - "dive": "0.9.2", - }, - }, - { - name: "find dpkg packages", - pkgType: pkg.DebPkg, - pkgInfo: map[string]string{ - "apt": "1.8.2", - }, - }, - { - name: "find java packages", - pkgType: pkg.JavaPkg, - pkgLanguage: pkg.Java, - pkgInfo: map[string]string{ - "example-java-app-maven": "0.1.0", - "example-jenkins-plugin": "1.0-SNAPSHOT", // the jenkins HPI file has a nested JAR of the same name - }, - }, - { - name: "find jenkins plugins", - pkgType: pkg.JenkinsPluginPkg, - pkgLanguage: pkg.Java, - pkgInfo: map[string]string{ - "example-jenkins-plugin": "1.0-SNAPSHOT", - }, - }, - { - name: "find python wheel packages", - pkgType: pkg.WheelPkg, - pkgLanguage: pkg.Python, - pkgInfo: map[string]string{ - "Pygments": "2.6.1", - "requests": "2.10.0", - }, - }, - { - name: "find javascript npm packages", - pkgType: pkg.NpmPkg, - pkgLanguage: pkg.JavaScript, - pkgInfo: map[string]string{ - "get-stdin": "8.0.0", - }, - }, - { - name: "find javascript yarn packages", - pkgType: pkg.YarnPkg, - pkgLanguage: pkg.JavaScript, - pkgInfo: map[string]string{ - "@babel/code-frame": "7.10.4", - }, - }, - { - name: "find python egg packages", - pkgType: pkg.EggPkg, - pkgLanguage: pkg.Python, - pkgInfo: map[string]string{ - "requests": "2.22.0", - "otherpkg": "2.19.0", - }, - }, - { - name: "find python packages", - pkgType: pkg.PythonRequirementsPkg, - pkgLanguage: pkg.Python, - pkgInfo: map[string]string{ - "flask": "4.0.0", - }, - }, - { - name: "find bundler packages", - pkgType: pkg.BundlerPkg, - pkgLanguage: pkg.Ruby, - pkgInfo: map[string]string{ - "actionmailer": "4.1.1", - "actionpack": "4.1.1", - "actionview": "4.1.1", - "activemodel": "4.1.1", - "activerecord": "4.1.1", - "activesupport": "4.1.1", - "arel": "5.0.1.20140414130214", - "bootstrap-sass": "3.1.1.1", - "builder": "3.2.2", - "coffee-rails": "4.0.1", - "coffee-script": "2.2.0", - "coffee-script-source": "1.7.0", - "erubis": "2.7.0", - "execjs": "2.0.2", - "hike": "1.2.3", - "i18n": "0.6.9", - "jbuilder": "2.0.7", - "jquery-rails": "3.1.0", - "json": "1.8.1", - "kgio": "2.9.2", - "libv8": "3.16.14.3", - "mail": "2.5.4", - "mime-types": "1.25.1", - "minitest": "5.3.4", - "multi_json": "1.10.1", - "mysql2": "0.3.16", - "polyglot": "0.3.4", - "rack": "1.5.2", - "rack-test": "0.6.2", - "rails": "4.1.1", - "railties": "4.1.1", - "raindrops": "0.13.0", - "rake": "10.3.2", - "rdoc": "4.1.1", - "ref": "1.0.5", - "sass": "3.2.19", - "sass-rails": "4.0.3", - "sdoc": "0.4.0", - "spring": "1.1.3", - "sprockets": "2.11.0", - "sprockets-rails": "2.1.3", - "sqlite3": "1.3.9", - "therubyracer": "0.12.1", - "thor": "0.19.1", - "thread_safe": "0.3.3", - "tilt": "1.4.1", - "treetop": "1.4.15", - "turbolinks": "2.2.2", - "tzinfo": "1.2.0", - "uglifier": "2.5.0", - "unicorn": "4.8.3", - }, - }, - { - - name: "find apkdb packages", - pkgType: pkg.ApkPkg, - pkgInfo: map[string]string{ - "musl-utils": "1.1.24-r2", - "libc-utils": "0.7.2-r0", - }, - }, - { - name: "find golang modules", - pkgType: pkg.GoModulePkg, - pkgLanguage: pkg.Go, - pkgInfo: map[string]string{ - "github.com/bmatcuk/doublestar": "v1.3.1", - }, - }, -} - func TestPkgCoverageImage(t *testing.T) { fixtureImageName := "image-pkg-coverage" _, cleanup := testutils.GetFixtureImage(t, "docker-archive", fixtureImageName) diff --git a/test/integration/test-fixtures/snapshot/TestDirJsonPresenter.golden b/test/integration/test-fixtures/snapshot/TestDirJsonPresenter.golden deleted file mode 100644 index 9406dbebc1a..00000000000 --- a/test/integration/test-fixtures/snapshot/TestDirJsonPresenter.golden +++ /dev/null @@ -1 +0,0 @@ -{"artifacts":[{"name":"actionmailer","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"actionpack","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"actionview","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"activemodel","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"activerecord","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"activesupport","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"arel","version":"5.0.1.20140414130214","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"bootstrap-sass","version":"3.1.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"builder","version":"3.2.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"coffee-rails","version":"4.0.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"coffee-script","version":"2.2.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"coffee-script-source","version":"1.7.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"erubis","version":"2.7.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"execjs","version":"2.0.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"hike","version":"1.2.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"i18n","version":"0.6.9","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"jbuilder","version":"2.0.7","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"jquery-rails","version":"3.1.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"json","version":"1.8.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"kgio","version":"2.9.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"libv8","version":"3.16.14.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"mail","version":"2.5.4","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"mime-types","version":"1.25.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"minitest","version":"5.3.4","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"multi_json","version":"1.10.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"mysql2","version":"0.3.16","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"polyglot","version":"0.3.4","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"rack","version":"1.5.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"rack-test","version":"0.6.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"rails","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"railties","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"raindrops","version":"0.13.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"rake","version":"10.3.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"rdoc","version":"4.1.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"ref","version":"1.0.5","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"sass","version":"3.2.19","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"sass-rails","version":"4.0.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"sdoc","version":"0.4.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"spring","version":"1.1.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"sprockets","version":"2.11.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"sprockets-rails","version":"2.1.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"sqlite3","version":"1.3.9","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"therubyracer","version":"0.12.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"thor","version":"0.19.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"thread_safe","version":"0.3.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"tilt","version":"1.4.1","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"treetop","version":"1.4.15","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"turbolinks","version":"2.2.2","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"tzinfo","version":"1.2.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"uglifier","version":"2.5.0","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null},{"name":"unicorn","version":"4.8.3","type":"bundle","cataloger":"","sources":[{"foundBy":"bundler-cataloger","effects":[]}],"metadata":null}],"Source":"test-fixtures"} \ No newline at end of file diff --git a/test/integration/test-fixtures/snapshot/TestDirTextPresenter.golden b/test/integration/test-fixtures/snapshot/TestDirTextPresenter.golden deleted file mode 100644 index de5364a329c..00000000000 --- a/test/integration/test-fixtures/snapshot/TestDirTextPresenter.golden +++ /dev/null @@ -1,256 +0,0 @@ -[Path: test-fixtures] -[actionmailer] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[actionpack] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[actionview] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[activemodel] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[activerecord] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[activesupport] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[arel] - Version: 5.0.1.20140414130214 - Type: bundle - Found by: bundler-cataloger - -[bootstrap-sass] - Version: 3.1.1.1 - Type: bundle - Found by: bundler-cataloger - -[builder] - Version: 3.2.2 - Type: bundle - Found by: bundler-cataloger - -[coffee-rails] - Version: 4.0.1 - Type: bundle - Found by: bundler-cataloger - -[coffee-script] - Version: 2.2.0 - Type: bundle - Found by: bundler-cataloger - -[coffee-script-source] - Version: 1.7.0 - Type: bundle - Found by: bundler-cataloger - -[erubis] - Version: 2.7.0 - Type: bundle - Found by: bundler-cataloger - -[execjs] - Version: 2.0.2 - Type: bundle - Found by: bundler-cataloger - -[hike] - Version: 1.2.3 - Type: bundle - Found by: bundler-cataloger - -[i18n] - Version: 0.6.9 - Type: bundle - Found by: bundler-cataloger - -[jbuilder] - Version: 2.0.7 - Type: bundle - Found by: bundler-cataloger - -[jquery-rails] - Version: 3.1.0 - Type: bundle - Found by: bundler-cataloger - -[json] - Version: 1.8.1 - Type: bundle - Found by: bundler-cataloger - -[kgio] - Version: 2.9.2 - Type: bundle - Found by: bundler-cataloger - -[libv8] - Version: 3.16.14.3 - Type: bundle - Found by: bundler-cataloger - -[mail] - Version: 2.5.4 - Type: bundle - Found by: bundler-cataloger - -[mime-types] - Version: 1.25.1 - Type: bundle - Found by: bundler-cataloger - -[minitest] - Version: 5.3.4 - Type: bundle - Found by: bundler-cataloger - -[multi_json] - Version: 1.10.1 - Type: bundle - Found by: bundler-cataloger - -[mysql2] - Version: 0.3.16 - Type: bundle - Found by: bundler-cataloger - -[polyglot] - Version: 0.3.4 - Type: bundle - Found by: bundler-cataloger - -[rack] - Version: 1.5.2 - Type: bundle - Found by: bundler-cataloger - -[rack-test] - Version: 0.6.2 - Type: bundle - Found by: bundler-cataloger - -[rails] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[railties] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[raindrops] - Version: 0.13.0 - Type: bundle - Found by: bundler-cataloger - -[rake] - Version: 10.3.2 - Type: bundle - Found by: bundler-cataloger - -[rdoc] - Version: 4.1.1 - Type: bundle - Found by: bundler-cataloger - -[ref] - Version: 1.0.5 - Type: bundle - Found by: bundler-cataloger - -[sass] - Version: 3.2.19 - Type: bundle - Found by: bundler-cataloger - -[sass-rails] - Version: 4.0.3 - Type: bundle - Found by: bundler-cataloger - -[sdoc] - Version: 0.4.0 - Type: bundle - Found by: bundler-cataloger - -[spring] - Version: 1.1.3 - Type: bundle - Found by: bundler-cataloger - -[sprockets] - Version: 2.11.0 - Type: bundle - Found by: bundler-cataloger - -[sprockets-rails] - Version: 2.1.3 - Type: bundle - Found by: bundler-cataloger - -[sqlite3] - Version: 1.3.9 - Type: bundle - Found by: bundler-cataloger - -[therubyracer] - Version: 0.12.1 - Type: bundle - Found by: bundler-cataloger - -[thor] - Version: 0.19.1 - Type: bundle - Found by: bundler-cataloger - -[thread_safe] - Version: 0.3.3 - Type: bundle - Found by: bundler-cataloger - -[tilt] - Version: 1.4.1 - Type: bundle - Found by: bundler-cataloger - -[treetop] - Version: 1.4.15 - Type: bundle - Found by: bundler-cataloger - -[turbolinks] - Version: 2.2.2 - Type: bundle - Found by: bundler-cataloger - -[tzinfo] - Version: 1.2.0 - Type: bundle - Found by: bundler-cataloger - -[uglifier] - Version: 2.5.0 - Type: bundle - Found by: bundler-cataloger - -[unicorn] - Version: 4.8.3 - Type: bundle - Found by: bundler-cataloger -