mirror of
https://github.com/anchore/syft.git
synced 2026-10-12 22:27:22 +02:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78ad3d648f | ||
|
|
1b3e57c264 | ||
|
|
ad7edba2a5 | ||
|
|
e51d4bcda1 | ||
|
|
d8113b5f07 | ||
|
|
a35f64c971 | ||
|
|
5534c38d0f | ||
|
|
47fc909700 | ||
|
|
2e2a9377ea | ||
|
|
4ca79c7626 | ||
|
|
3743f5ae53 | ||
|
|
ebb9d4edb6 | ||
|
|
e2a9d891b2 | ||
|
|
5b09c154bb |
@@ -17,7 +17,7 @@ jobs:
|
||||
# the job by event.
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- run: python .github/scripts/labeler.py
|
||||
env:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
with:
|
||||
# we need to be able to get the commit for a tag
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
environment: release
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Check if tag already exists
|
||||
# note: this will fail if the tag already exists
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
# required for goreleaser signs section with cosign
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc #v6.0.1
|
||||
- uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e #v6.0.2
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc #v6.0.1
|
||||
- uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e #v6.0.2
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 #v5.0.0
|
||||
with:
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@a4f52f8033a6168103c2538976c07b467e8163bc #v6.0.1
|
||||
- uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e #v6.0.2
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
name: "Static analysis"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -39,8 +39,14 @@ jobs:
|
||||
- name: Restore file executable test-fixture cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 #v4.0.1
|
||||
with:
|
||||
path: syft/file/cataloger/executable/test-fixtures/bin
|
||||
key: ${{ runner.os }}-unit-file-executable-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/cache.fingerprint' ) }}
|
||||
path: syft/file/cataloger/executable/test-fixtures/elf/bin
|
||||
key: ${{ runner.os }}-unit-file-executable-elf-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/elf/cache.fingerprint' ) }}
|
||||
|
||||
- name: Restore file executable shared-info test-fixture cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 #v4.0.1
|
||||
with:
|
||||
path: syft/file/cataloger/executable/test-fixtures/shared-info/bin
|
||||
key: ${{ runner.os }}-unit-file-executable-shared-info-cache-${{ hashFiles( 'syft/file/cataloger/executable/test-fixtures/shared-info/cache.fingerprint' ) }}
|
||||
|
||||
- name: Restore Java test-fixture cache
|
||||
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 #v4.0.1
|
||||
@@ -81,7 +87,7 @@ jobs:
|
||||
name: "Integration tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -103,7 +109,7 @@ jobs:
|
||||
name: "Build snapshot artifacts"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -133,7 +139,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -181,7 +187,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -226,7 +232,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 #v4.1.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
+6
-3
@@ -266,7 +266,8 @@ tasks:
|
||||
desc: Generate test fixture fingerprints
|
||||
generates:
|
||||
- cmd/syft/internal/test/integration/test-fixtures/cache.fingerprint
|
||||
- syft/file/cataloger/executable/test-fixtures/cache.fingerprint
|
||||
- syft/file/cataloger/executable/test-fixtures/elf/cache.fingerprint
|
||||
- syft/file/cataloger/executable/test-fixtures/shared-info/cache.fingerprint
|
||||
- syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint
|
||||
- syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint
|
||||
- syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint
|
||||
@@ -276,7 +277,8 @@ tasks:
|
||||
- test/cli/test-fixtures/cache.fingerprint
|
||||
cmds:
|
||||
# for EXECUTABLE unit test fixtures
|
||||
- "cd syft/file/cataloger/executable/test-fixtures && make cache.fingerprint"
|
||||
- "cd syft/file/cataloger/executable/test-fixtures/elf && make cache.fingerprint"
|
||||
- "cd syft/file/cataloger/executable/test-fixtures/shared-info && make cache.fingerprint"
|
||||
# for IMAGE integration test fixtures
|
||||
- "cd cmd/syft/internal/test/integration/test-fixtures && make cache.fingerprint"
|
||||
# for BINARY unit test fixtures
|
||||
@@ -297,7 +299,8 @@ tasks:
|
||||
fixtures:
|
||||
desc: Generate test fixtures
|
||||
cmds:
|
||||
- "cd syft/file/cataloger/executable/test-fixtures && make"
|
||||
- "cd syft/file/cataloger/executable/test-fixtures/elf && make"
|
||||
- "cd syft/file/cataloger/executable/test-fixtures/shared-info && make"
|
||||
- "cd syft/pkg/cataloger/java/test-fixtures/java-builds && make"
|
||||
- "cd syft/pkg/cataloger/redhat/test-fixtures && make"
|
||||
- "cd syft/pkg/cataloger/binary/test-fixtures && make"
|
||||
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/dave/jennifer v1.7.0
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/distribution/reference v0.5.0
|
||||
github.com/docker/docker v25.0.3+incompatible
|
||||
github.com/docker/docker v25.0.4+incompatible
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/github/go-spdx/v2 v2.2.0
|
||||
@@ -48,7 +48,7 @@ require (
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.4
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
|
||||
github.com/knqyf263/go-rpmdb v0.1.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
@@ -79,7 +79,7 @@ require (
|
||||
golang.org/x/mod v0.16.0
|
||||
golang.org/x/net v0.22.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.29.2
|
||||
modernc.org/sqlite v1.29.3
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -223,8 +223,8 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe
|
||||
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
|
||||
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v25.0.4+incompatible h1:XITZTrq+52tZyZxUOtFIahUf3aH367FLxJzt9vZeAF8=
|
||||
github.com/docker/docker v25.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
@@ -495,8 +495,8 @@ github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6K
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b h1:boYyvL3tbUuKcMN029mpCl7oYYJ7yIXujLj+fiW4Alc=
|
||||
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww=
|
||||
github.com/knqyf263/go-rpmdb v0.1.0 h1:pOgjtOGtW0B+ibY905hP3ETrYFmLZsHiReKsplcs+to=
|
||||
github.com/knqyf263/go-rpmdb v0.1.0/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@@ -542,8 +542,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
@@ -1345,6 +1345,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||
@@ -1353,8 +1355,8 @@ modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/sqlite v1.29.2 h1:xgBSyA3gemwgP31PWFfFjtBorQNYpeypGdoSDjXhrgI=
|
||||
modernc.org/sqlite v1.29.2/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0=
|
||||
modernc.org/sqlite v1.29.3 h1:6L71d3zXVB8oubdVSuwiurNyYRetQ3It8l1FSwylwQ0=
|
||||
modernc.org/sqlite v1.29.3/go.mod h1:MjUIBKZ+tU/lqjNLbVAAMjsQPdWdA/ciwdhsT9kBwk8=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.4"
|
||||
JSONSchemaVersion = "16.0.6"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func NewDependencyRelationships(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.Relationship {
|
||||
// TODO: consider library format (e.g. ELF, Mach-O, PE) for the meantime assume all binaries are homogeneous format
|
||||
|
||||
// start with building new file-to-file relationships for executables-to-executables
|
||||
// you need to make certain that they are unique, store in a map[id]map[id]relationship to avoid dupes.
|
||||
// before creating the new file-to-file relationship, check to see if there are packages that represent each
|
||||
// file. If there are, create a package-to-package, file-to-package, or package-to-file relationship as appropriate.
|
||||
|
||||
// 1 & 2... build an index of all shared libraries and their owning packages to search against
|
||||
index := newShareLibIndex(resolver, accessor)
|
||||
|
||||
// 3. craft package-to-package or package-to-file relationships that represent binary shared library dependencies
|
||||
// note: prefer package-to-package relationships over package-to-file relationships
|
||||
|
||||
relIndex := newRelationshipIndex()
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
// read all existing dependencyOf relationships
|
||||
for _, r := range s.Relationships {
|
||||
if r.Type != artifact.DependencyOfRelationship {
|
||||
continue
|
||||
}
|
||||
relIndex.track(r)
|
||||
}
|
||||
})
|
||||
|
||||
// find all new relationships to add...
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
for _, parentPkg := range s.Artifacts.Packages.Sorted(pkg.BinaryPkg) {
|
||||
for _, evidentLocation := range parentPkg.Locations.ToSlice() {
|
||||
if evidentLocation.Annotations[pkg.EvidenceAnnotationKey] != pkg.PrimaryEvidenceAnnotation {
|
||||
continue
|
||||
}
|
||||
|
||||
// find all libraries that this package depends on
|
||||
exec, ok := s.Artifacts.Executables[evidentLocation.Coordinates]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, libReference := range exec.ImportedLibraries {
|
||||
// TODO: is this always a basename? technically no, it could be a path...
|
||||
libBasename := path.Base(libReference)
|
||||
|
||||
pkgsThatOwnLib := index.owningLibraryPackage(libBasename)
|
||||
if pkgsThatOwnLib == nil {
|
||||
// create package-to-file relationship...
|
||||
// if there is more than one library for this given library name, then we will include
|
||||
// all of them as dependencies since we don't know the LD_LIBRARY_PATH order
|
||||
// TODO: add configuration for LD_LIBRARY_PATH order?
|
||||
for _, libCoord := range index.owningLibraryLocations(libBasename).ToSlice() {
|
||||
relIndex.add(
|
||||
artifact.Relationship{
|
||||
From: libCoord,
|
||||
To: parentPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// don't create a package-to-package relationship for this library... since we can't
|
||||
continue
|
||||
}
|
||||
|
||||
// create a package-to-package relationship between the binary package and the library package
|
||||
// if there is more than one library for this given library name, then we will include
|
||||
// all of them as dependencies since we don't know the LD_LIBRARY_PATH order
|
||||
for _, pkgThatOwnsLib := range pkgsThatOwnLib.Sorted() {
|
||||
relIndex.add(
|
||||
artifact.Relationship{
|
||||
From: pkgThatOwnsLib,
|
||||
To: parentPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// so far this handles the first order dependencies from the binary package. Odds are that the OS package manager
|
||||
// will have already created a package-to-package relationship for the lib packages to other lib packages.
|
||||
|
||||
return relIndex.newRelationships()
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
)
|
||||
|
||||
type relationshipIndex struct {
|
||||
typesByFromTo map[artifact.ID]map[artifact.ID]*strset.Set
|
||||
additional []artifact.Relationship
|
||||
}
|
||||
|
||||
func newRelationshipIndex(existing ...artifact.Relationship) *relationshipIndex {
|
||||
r := &relationshipIndex{
|
||||
typesByFromTo: make(map[artifact.ID]map[artifact.ID]*strset.Set),
|
||||
additional: make([]artifact.Relationship, 0),
|
||||
}
|
||||
for _, rel := range existing {
|
||||
r.track(rel)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// track this relationship as "exists" in the index (this is used to prevent duplicate relationships from being added).
|
||||
// returns true if the relationship is new to the index, false otherwise.
|
||||
func (i *relationshipIndex) track(r artifact.Relationship) bool {
|
||||
fromID := r.From.ID()
|
||||
if _, ok := i.typesByFromTo[fromID]; !ok {
|
||||
i.typesByFromTo[fromID] = make(map[artifact.ID]*strset.Set)
|
||||
}
|
||||
|
||||
toID := r.To.ID()
|
||||
if _, ok := i.typesByFromTo[fromID][toID]; !ok {
|
||||
i.typesByFromTo[fromID][toID] = strset.New()
|
||||
}
|
||||
|
||||
var exists bool
|
||||
if i.typesByFromTo[fromID][toID].Has(string(r.Type)) {
|
||||
exists = true
|
||||
}
|
||||
|
||||
i.typesByFromTo[fromID][toID].Add(string(r.Type))
|
||||
return exists
|
||||
}
|
||||
|
||||
// add a new relationship to the index, returning true if the relationship is new to the index, false otherwise (thus is a duplicate).
|
||||
func (i *relationshipIndex) add(r artifact.Relationship) bool {
|
||||
if !i.track(r) {
|
||||
i.additional = append(i.additional, r)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *relationshipIndex) newRelationships() []artifact.Relationship {
|
||||
return i.additional
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
type sharedLibraryIndex struct {
|
||||
libLocationsByBasename map[string]file.CoordinateSet
|
||||
allLibLocations file.CoordinateSet
|
||||
packagesByLibraryPath map[file.Coordinates]*pkg.Collection
|
||||
}
|
||||
|
||||
func newShareLibIndex(resolver file.Resolver, accessor sbomsync.Accessor) *sharedLibraryIndex {
|
||||
s := &sharedLibraryIndex{
|
||||
libLocationsByBasename: make(map[string]file.CoordinateSet),
|
||||
allLibLocations: file.NewCoordinateSet(),
|
||||
packagesByLibraryPath: make(map[file.Coordinates]*pkg.Collection),
|
||||
}
|
||||
|
||||
s.build(resolver, accessor)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (i *sharedLibraryIndex) build(resolver file.Resolver, accessor sbomsync.Accessor) {
|
||||
// 1. map out all locations that provide libraries (indexed by the basename)
|
||||
i.libLocationsByBasename, i.allLibLocations = locationsThatProvideLibraries(accessor)
|
||||
|
||||
// 2. for each library path, find all packages that claim ownership of the library
|
||||
i.packagesByLibraryPath = packagesWithLibraryOwnership(resolver, accessor, i.allLibLocations)
|
||||
}
|
||||
|
||||
func (i *sharedLibraryIndex) owningLibraryLocations(libraryBasename string) file.CoordinateSet {
|
||||
if set, ok := i.libLocationsByBasename[libraryBasename]; ok {
|
||||
return set
|
||||
}
|
||||
|
||||
return file.NewCoordinateSet()
|
||||
}
|
||||
|
||||
func (i *sharedLibraryIndex) owningLibraryPackage(libraryBasename string) *pkg.Collection {
|
||||
// find all packages that own a library by it's basename
|
||||
if set, ok := i.libLocationsByBasename[libraryBasename]; ok {
|
||||
for _, coord := range set.ToSlice() {
|
||||
if pkgSet, ok := i.packagesByLibraryPath[coord]; ok {
|
||||
return pkgSet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func locationsThatProvideLibraries(accessor sbomsync.Accessor) (map[string]file.CoordinateSet, file.CoordinateSet) {
|
||||
// map out all locations that provide libraries (indexed by the basename)
|
||||
libLocationsByBasename := make(map[string]file.CoordinateSet)
|
||||
allLibLocations := file.NewCoordinateSet()
|
||||
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
for coord, f := range s.Artifacts.Executables {
|
||||
if !f.HasExports {
|
||||
continue
|
||||
}
|
||||
|
||||
basename := path.Base(coord.RealPath)
|
||||
set := libLocationsByBasename[basename]
|
||||
set.Add(coord)
|
||||
allLibLocations.Add(coord)
|
||||
libLocationsByBasename[basename] = set
|
||||
}
|
||||
})
|
||||
|
||||
return libLocationsByBasename, allLibLocations
|
||||
}
|
||||
func packagesWithLibraryOwnership(resolver file.Resolver, accessor sbomsync.Accessor, allLibLocations file.CoordinateSet) map[file.Coordinates]*pkg.Collection {
|
||||
// map out all packages that claim ownership of a library at a specific path
|
||||
packagesByLibraryPath := make(map[file.Coordinates]*pkg.Collection)
|
||||
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
for _, p := range s.Artifacts.Packages.Sorted() {
|
||||
fileOwner, ok := p.Metadata.(pkg.FileOwner)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, pth := range fileOwner.OwnedFiles() {
|
||||
ownedLocation, err := resolver.FilesByPath(pth)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", pth).Trace("unable to find path for owned file")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, loc := range ownedLocation {
|
||||
// if the location is a library, add the package to the set of packages that own the library
|
||||
if !allLibLocations.Contains(loc.Coordinates) {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := packagesByLibraryPath[loc.Coordinates]; !ok {
|
||||
packagesByLibraryPath[loc.Coordinates] = pkg.NewCollection()
|
||||
}
|
||||
|
||||
// we have a library path, add the package to the set of packages that own the library
|
||||
packagesByLibraryPath[loc.Coordinates].Add(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return packagesByLibraryPath
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
package relationship
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/internal/relationship/binary"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func Finalize(builder sbomsync.Builder, cfg cataloging.RelationshipsConfig, src artifact.Identifiable) {
|
||||
func Finalize(resolver file.Resolver, builder sbomsync.Builder, cfg cataloging.RelationshipsConfig, src artifact.Identifiable) {
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
// remove ELF packages that are already represented by a non-ELF package
|
||||
// TODO (also, how should we update the TUI to reflect that we removed packages?)
|
||||
|
||||
// add relationships showing packages that are evident by a file which is owned by another package (package-to-package)
|
||||
if cfg.PackageFileOwnershipOverlap {
|
||||
byFileOwnershipOverlapWorker(accessor)
|
||||
@@ -21,6 +26,12 @@ func Finalize(builder sbomsync.Builder, cfg cataloging.RelationshipsConfig, src
|
||||
excludeBinariesByFileOwnershipOverlap(accessor)
|
||||
}
|
||||
|
||||
// add the new relationships for executables to the SBOM
|
||||
newBinaryRelationships := binary.NewDependencyRelationships(resolver, accessor)
|
||||
accessor.WriteToSBOM(func(s *sbom.SBOM) {
|
||||
s.Relationships = append(s.Relationships, newBinaryRelationships...)
|
||||
})
|
||||
|
||||
// add source "contains package" relationship (source-to-package)
|
||||
var sourceRelationships []artifact.Relationship
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
|
||||
@@ -117,6 +117,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
},
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
|
||||
),
|
||||
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package"),
|
||||
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||
newPackageTaskFactory(
|
||||
|
||||
@@ -22,8 +22,9 @@ func (s sourceIdentifierAdapter) ID() artifact.ID {
|
||||
}
|
||||
|
||||
func NewRelationshipsTask(cfg cataloging.RelationshipsConfig, src source.Description) Task {
|
||||
fn := func(_ context.Context, _ file.Resolver, builder sbomsync.Builder) error {
|
||||
fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
relationship.Finalize(
|
||||
resolver,
|
||||
builder,
|
||||
cfg,
|
||||
&sourceIdentifierAdapter{desc: src})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.4/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.6/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -630,6 +630,26 @@
|
||||
"dso"
|
||||
]
|
||||
},
|
||||
"ElfBinaryPackageNoteJsonPayload": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"vendor": {
|
||||
"type": "string"
|
||||
},
|
||||
"system": {
|
||||
"type": "string"
|
||||
},
|
||||
"sourceRepo": {
|
||||
"type": "string"
|
||||
},
|
||||
"commit": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ElixirMixLockEntry": {
|
||||
"properties": {
|
||||
"name": {
|
||||
@@ -681,13 +701,28 @@
|
||||
"format": {
|
||||
"type": "string"
|
||||
},
|
||||
"hasExports": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"hasEntrypoint": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"importedLibraries": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"elfSecurityFeatures": {
|
||||
"$ref": "#/$defs/ELFSecurityFeatures"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"format"
|
||||
"format",
|
||||
"hasExports",
|
||||
"hasEntrypoint",
|
||||
"importedLibraries"
|
||||
]
|
||||
},
|
||||
"File": {
|
||||
@@ -1410,6 +1445,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/DpkgDbEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/ElfBinaryPackageNoteJsonPayload"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/ElixirMixLockEntry"
|
||||
},
|
||||
|
||||
@@ -155,13 +155,25 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
|
||||
|
||||
data.Format = format
|
||||
|
||||
securityFeatures, err := findSecurityFeatures(format, reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine security features for %q", loc.RealPath)
|
||||
return nil, nil
|
||||
switch format {
|
||||
case file.ELF:
|
||||
if err := findELFFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine ELF features for %q", loc.RealPath)
|
||||
}
|
||||
case file.PE:
|
||||
if err := findPEFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine PE features for %q", loc.RealPath)
|
||||
}
|
||||
case file.MachO:
|
||||
if err := findMachoFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine Macho features for %q", loc.RealPath)
|
||||
}
|
||||
}
|
||||
|
||||
data.SecurityFeatures = securityFeatures
|
||||
// always allocate collections for presentation
|
||||
if data.ImportedLibraries == nil {
|
||||
data.ImportedLibraries = []string{}
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
}
|
||||
@@ -230,18 +242,3 @@ func isPE(by []byte) bool {
|
||||
func isELF(by []byte) bool {
|
||||
return bytes.HasPrefix(by, []byte(elf.ELFMAG))
|
||||
}
|
||||
|
||||
func findSecurityFeatures(format file.ExecutableFormat, reader unionreader.UnionReader) (*file.ELFSecurityFeatures, error) {
|
||||
// TODO: add support for PE and MachO
|
||||
switch format { //nolint: gocritic
|
||||
case file.ELF:
|
||||
return findELFSecurityFeatures(reader) //nolint: gocritic
|
||||
case file.PE:
|
||||
// return findPESecurityFeatures(reader)
|
||||
return nil, nil
|
||||
case file.MachO:
|
||||
// return findMachOSecurityFeatures(reader)
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported executable format: %q", format)
|
||||
}
|
||||
|
||||
@@ -12,13 +12,33 @@ import (
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
func findELFSecurityFeatures(reader unionreader.UnionReader) (*file.ELFSecurityFeatures, error) {
|
||||
func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) error {
|
||||
f, err := elf.NewFile(reader)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
return err
|
||||
}
|
||||
|
||||
features := file.ELFSecurityFeatures{
|
||||
libs, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read imported libraries from elf file")
|
||||
libs = nil
|
||||
}
|
||||
|
||||
if libs == nil {
|
||||
libs = []string{}
|
||||
}
|
||||
|
||||
data.ImportedLibraries = libs
|
||||
data.ELFSecurityFeatures = findELFSecurityFeatures(f)
|
||||
data.HasEntrypoint = elfHasEntrypoint(f)
|
||||
data.HasExports = elfHasExports(f)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findELFSecurityFeatures(f *elf.File) *file.ELFSecurityFeatures {
|
||||
return &file.ELFSecurityFeatures{
|
||||
SymbolTableStripped: isElfSymbolTableStripped(f),
|
||||
StackCanary: checkElfStackCanary(f),
|
||||
NoExecutable: checkElfNXProtection(f),
|
||||
@@ -29,8 +49,6 @@ func findELFSecurityFeatures(reader unionreader.UnionReader) (*file.ELFSecurityF
|
||||
LlvmControlFlowIntegrity: checkLLVMControlFlowIntegrity(f),
|
||||
ClangFortifySource: checkClangFortifySource(f),
|
||||
}
|
||||
|
||||
return &features, nil
|
||||
}
|
||||
|
||||
func isElfSymbolTableStripped(file *elf.File) bool {
|
||||
@@ -219,3 +237,34 @@ func checkClangFortifySource(file *elf.File) *bool {
|
||||
}
|
||||
return boolRef(false)
|
||||
}
|
||||
|
||||
func elfHasEntrypoint(f *elf.File) bool {
|
||||
// this is akin to
|
||||
// readelf -h ./path/to/bin | grep "Entry point address"
|
||||
return f.Entry > 0
|
||||
}
|
||||
|
||||
func elfHasExports(f *elf.File) bool {
|
||||
// this is akin to:
|
||||
// nm -D --defined-only ./path/to/bin | grep ' T \| W \| B '
|
||||
// where:
|
||||
// T - symbol in the text section
|
||||
// W - weak symbol that might be overwritten
|
||||
// B - variable located in the uninitialized data section
|
||||
// really anything that is not marked with 'U' (undefined) is considered an export.
|
||||
symbols, err := f.DynamicSymbols()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns?
|
||||
return false
|
||||
}
|
||||
|
||||
for _, s := range symbols {
|
||||
// check if the section is SHN_UNDEF, which is the "U" output type from "nm -D" meaning that the symbol
|
||||
// is undefined, meaning it is not an export. Any entry that is not undefined is considered an export.
|
||||
if s.Section != elf.SHN_UNDEF {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@@ -16,7 +18,7 @@ func Test_findELFSecurityFeatures(t *testing.T) {
|
||||
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures", fixture))
|
||||
f, err := os.Open(filepath.Join("test-fixtures/elf", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
@@ -26,7 +28,6 @@ func Test_findELFSecurityFeatures(t *testing.T) {
|
||||
fixture string
|
||||
want *file.ELFSecurityFeatures
|
||||
wantStripped bool
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "detect canary",
|
||||
@@ -145,14 +146,10 @@ func Test_findELFSecurityFeatures(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
got, err := findELFSecurityFeatures(readerForFixture(t, tt.fixture))
|
||||
tt.wantErr(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
|
||||
got := findELFSecurityFeatures(f)
|
||||
|
||||
if d := cmp.Diff(tt.want, got); d != "" {
|
||||
t.Errorf("findELFSecurityFeatures() mismatch (-want +got):\n%s", d)
|
||||
@@ -160,3 +157,70 @@ func Test_findELFSecurityFeatures(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_elfHasEntrypoint(t *testing.T) {
|
||||
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "shared lib",
|
||||
fixture: "bin/libhello.so",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
fixture: "bin/hello_linux",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, elfHasEntrypoint(f))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_elfHasExports(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "shared lib",
|
||||
fixture: "bin/libhello.so",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
fixture: "bin/hello_linux",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, elfHasExports(f))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
// source http://www.cilinder.be/docs/next/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/index.html
|
||||
const (
|
||||
machoNPExt uint8 = 0x10 /* N_PEXT: private external symbol bit */
|
||||
machoNExt uint8 = 0x01 /* N_EXT: external symbol bit, set for external symbols */
|
||||
// > #define LC_REQ_DYLD 0x80000000
|
||||
// > #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */
|
||||
lcMain = 0x28 | 0x80000000
|
||||
)
|
||||
|
||||
func findMachoFeatures(data *file.Executable, reader unionreader.UnionReader) error {
|
||||
// TODO: support security features
|
||||
|
||||
// TODO: support multi-architecture binaries
|
||||
f, err := macho.NewFile(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
libs, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.ImportedLibraries = libs
|
||||
data.HasEntrypoint = machoHasEntrypoint(f)
|
||||
data.HasExports = machoHasExports(f)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func machoHasEntrypoint(f *macho.File) bool {
|
||||
// derived from struct entry_point_command found from which explicitly calls out LC_MAIN:
|
||||
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
|
||||
// we need to look for both LC_MAIN and LC_UNIXTHREAD commands to determine if the file is an executable
|
||||
//
|
||||
// this is akin to:
|
||||
// otool -l ./path/to/bin | grep -A4 LC_MAIN
|
||||
// otool -l ./path/to/bin | grep -A4 LC_UNIXTHREAD
|
||||
for _, l := range f.Loads {
|
||||
data := l.Raw()
|
||||
cmd := f.ByteOrder.Uint32(data)
|
||||
|
||||
if macho.LoadCmd(cmd) == macho.LoadCmdUnixThread || macho.LoadCmd(cmd) == lcMain {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func machoHasExports(f *macho.File) bool {
|
||||
for _, sym := range f.Symtab.Syms {
|
||||
// look for symbols that are:
|
||||
// - not private and are external
|
||||
// - do not have an N_TYPE value of N_UNDF (undefined symbol)
|
||||
//
|
||||
// here's the bit layout for the n_type field:
|
||||
// 0000 0000
|
||||
// ─┬─│ ─┬─│
|
||||
// │ │ │ └─ N_EXT (external symbol)
|
||||
// │ │ └─ N_TYPE (N_UNDF, N_ABS, N_SECT, N_PBUD, N_INDR)
|
||||
// │ └─ N_PEXT (private external symbol)
|
||||
// └─ N_STAB (debugging symbol)
|
||||
//
|
||||
isExternal := sym.Type&machoNExt == machoNExt
|
||||
isPrivate := sym.Type&machoNPExt == machoNPExt
|
||||
nTypeIsUndefined := sym.Type&0x0e == 0
|
||||
|
||||
if isExternal && !isPrivate {
|
||||
if sym.Name == "_main" || sym.Name == "__mh_execute_header" {
|
||||
// ...however there are some symbols that are not exported but are still important
|
||||
// for debugging or as an entrypoint, so we need to explicitly check for them
|
||||
continue
|
||||
}
|
||||
if nTypeIsUndefined {
|
||||
continue
|
||||
}
|
||||
// we have a symbol that is not private and is external
|
||||
// and is not undefined, so it is an export
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
func Test_machoHasEntrypoint(t *testing.T) {
|
||||
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "shared lib",
|
||||
fixture: "bin/libhello.dylib",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
fixture: "bin/hello_mac",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := macho.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, machoHasEntrypoint(f))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_machoHasExports(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "shared lib",
|
||||
fixture: "bin/libhello.dylib",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
fixture: "bin/hello_mac",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := macho.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, machoHasExports(f))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/pe"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
func findPEFeatures(data *file.Executable, reader unionreader.UnionReader) error {
|
||||
// TODO: support security features
|
||||
|
||||
f, err := pe.NewFile(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
libs, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data.ImportedLibraries = libs
|
||||
data.HasEntrypoint = peHasEntrypoint(f)
|
||||
data.HasExports = peHasExports(f)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
windowsExeEntrypoints = strset.New("main", "WinMain", "wWinMain")
|
||||
windowsDllEntrypoints = strset.New("DllMain", "_DllMainCRTStartup@12", "CRT_INIT")
|
||||
)
|
||||
|
||||
func peHasEntrypoint(f *pe.File) bool {
|
||||
// DLLs can have entrypoints, but they are not "executables" in the traditional sense,
|
||||
// but instead point to an initialization function (DLLMain).
|
||||
// The PE format does not require an entrypoint, so it is possible to not have one, however,
|
||||
// the microsoft C runtime does: https://learn.microsoft.com/en-US/troubleshoot/developer/visualstudio/cpp/libraries/use-c-run-time
|
||||
//
|
||||
// > When building a DLL which uses any of the C Run-time libraries, in order to ensure that the CRT is properly initialized, either
|
||||
// > 1. the initialization function must be named DllMain() and the entry point must be specified with the linker option -entry:_DllMainCRTStartup@12 - or -
|
||||
// > 2. the DLL's entry point must explicitly call CRT_INIT() on process attach and process detach
|
||||
//
|
||||
// This isn't really helpful from a user perspective when it comes to indicating if there is an entrypoint or not
|
||||
// since it will always effectively be true for DLLs! All DLLs and Executables (aka "modules") have a single
|
||||
// entrypoint, _GetPEImageBase, but we're more interested in the logical idea of an entrypoint.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/psapi/module-information for more details.
|
||||
|
||||
var hasLibEntrypoint, hasExeEntrypoint bool
|
||||
for _, s := range f.Symbols {
|
||||
if windowsExeEntrypoints.Has(s.Name) {
|
||||
hasExeEntrypoint = true
|
||||
}
|
||||
if windowsDllEntrypoints.Has(s.Name) {
|
||||
hasLibEntrypoint = true
|
||||
}
|
||||
}
|
||||
|
||||
switch v := f.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
return v.AddressOfEntryPoint > 0 && !hasLibEntrypoint && hasExeEntrypoint
|
||||
case *pe.OptionalHeader64:
|
||||
return v.AddressOfEntryPoint > 0 && !hasLibEntrypoint && hasExeEntrypoint
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func peHasExports(f *pe.File) bool {
|
||||
if f.OptionalHeader == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch v := f.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
return v.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size > 0
|
||||
case *pe.OptionalHeader64:
|
||||
return v.DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT].Size > 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"debug/pe"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
func Test_peHasEntrypoint(t *testing.T) {
|
||||
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "shared lib",
|
||||
fixture: "bin/hello.dll", // though this is a shared lib, it has an entrypoint (DLLMain)
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
fixture: "bin/hello.exe",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := pe.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, peHasEntrypoint(f))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_peHasExports(t *testing.T) {
|
||||
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
|
||||
t.Helper()
|
||||
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
|
||||
require.NoError(t, err)
|
||||
return f
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "shared lib",
|
||||
fixture: "bin/hello.dll",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "application",
|
||||
fixture: "bin/hello.exe",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := pe.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, peHasExports(f))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
../bin
|
||||
actual_verify
|
||||
Dockerfile.sha256
|
||||
@@ -0,0 +1 @@
|
||||
bin
|
||||
@@ -0,0 +1,16 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG OSXCROSS_VERSION=13.1
|
||||
ARG BUILDPLATFORM=linux/amd64
|
||||
FROM --platform=$BUILDPLATFORM crazymax/osxcross:${OSXCROSS_VERSION}-ubuntu AS osxcross
|
||||
|
||||
FROM --platform=$BUILDPLATFORM ubuntu:22.04
|
||||
|
||||
RUN apt update -y && apt install -y \
|
||||
curl wget \
|
||||
make gcc clang \
|
||||
git mingw-w64
|
||||
|
||||
COPY --from=osxcross /osxcross /osxcross
|
||||
ENV PATH="/osxcross/bin:$PATH"
|
||||
ENV LD_LIBRARY_PATH="/osxcross/lib:$LD_LIBRARY_PATH"
|
||||
@@ -0,0 +1,25 @@
|
||||
BIN=./bin
|
||||
TOOL_IMAGE=localhost/syft-shared-info-build-tools:latest
|
||||
VERIFY_FILE=actual_verify
|
||||
|
||||
all: build
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker inspect $(TOOL_IMAGE) > /dev/null && make tools-check) || (docker build -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
|
||||
build: tools
|
||||
mkdir -p $(BIN)
|
||||
docker run --platform linux/amd64 -i -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) make
|
||||
|
||||
debug:
|
||||
docker run --platform linux/amd64 -i --rm -v $(shell pwd):/mount -w /mount/project $(TOOL_IMAGE) bash
|
||||
|
||||
cache.fingerprint:
|
||||
@find project Dockerfile Makefile -type f -exec md5sum {} + | awk '{print $1}' | sort | tee cache.fingerprint
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/*
|
||||
|
||||
.PHONY: build verify debug build-image build-bins clean dockerfile-check cache.fingerprint
|
||||
@@ -0,0 +1,10 @@
|
||||
# invoke all make files in subdirectories
|
||||
.PHONY: all hello libhello
|
||||
|
||||
all: hello libhello
|
||||
|
||||
hello:
|
||||
$(MAKE) -C hello
|
||||
|
||||
libhello:
|
||||
$(MAKE) -C libhello
|
||||
@@ -0,0 +1,23 @@
|
||||
.PHONY: all linux windows mac
|
||||
|
||||
BIN=../../bin
|
||||
|
||||
all: $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac
|
||||
|
||||
linux: $(BIN)/libhello.so
|
||||
|
||||
windows: $(BIN)/libhello.dll
|
||||
|
||||
mac: $(BIN)/libhello.dylib
|
||||
|
||||
$(BIN)/hello_linux:
|
||||
gcc hello.c -o $(BIN)/hello_linux
|
||||
|
||||
$(BIN)/hello.exe:
|
||||
x86_64-w64-mingw32-gcc hello.c -o $(BIN)/hello.exe
|
||||
|
||||
$(BIN)/hello_mac:
|
||||
o64-clang hello.c -o $(BIN)/hello_mac
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
.PHONY: all linux windows mac
|
||||
|
||||
BIN=../../bin
|
||||
|
||||
all: $(BIN)/libhello.so $(BIN)/libhello.dll $(BIN)/libhello.dylib
|
||||
|
||||
linux: $(BIN)/libhello.so
|
||||
|
||||
windows: $(BIN)/libhello.dll
|
||||
|
||||
mac: $(BIN)/libhello.dylib
|
||||
|
||||
$(BIN)/libhello.so:
|
||||
gcc -shared -fPIC -o $(BIN)/libhello.so hello.c
|
||||
|
||||
$(BIN)/libhello.dll:
|
||||
x86_64-w64-mingw32-gcc -shared -o $(BIN)/hello.dll hello.c -Wl,--out-implib,$(BIN)/libhello.a
|
||||
|
||||
$(BIN)/libhello.dylib:
|
||||
o64-clang -dynamiclib -o $(BIN)/libhello.dylib hello.c
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)/libhello.so $(BIN)/hello.dll $(BIN)/libhello.dylib $(BIN)/libhello.a
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void hello() {
|
||||
printf("Hello from shared library!\n");
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef HELLO_H
|
||||
#define HELLO_H
|
||||
|
||||
void hello();
|
||||
|
||||
#endif
|
||||
@@ -19,7 +19,10 @@ type Executable struct {
|
||||
// Format denotes either ELF, Mach-O, or PE
|
||||
Format ExecutableFormat `json:"format" yaml:"format" mapstructure:"format"`
|
||||
|
||||
SecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
|
||||
HasExports bool `json:"hasExports" yaml:"hasExports" mapstructure:"hasExports"`
|
||||
HasEntrypoint bool `json:"hasEntrypoint" yaml:"hasEntrypoint" mapstructure:"hasEntrypoint"`
|
||||
ImportedLibraries []string `json:"importedLibraries" yaml:"importedLibraries" mapstructure:"importedLibraries"`
|
||||
ELFSecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
|
||||
}
|
||||
|
||||
type ELFSecurityFeatures struct {
|
||||
|
||||
@@ -224,7 +224,7 @@ func Test_encodeDecodeFileMetadata(t *testing.T) {
|
||||
Executables: map[file.Coordinates]file.Executable{
|
||||
c: {
|
||||
Format: file.ELF,
|
||||
SecurityFeatures: &file.ELFSecurityFeatures{
|
||||
ELFSecurityFeatures: &file.ELFSecurityFeatures{
|
||||
SymbolTableStripped: false,
|
||||
StackCanary: boolRef(true),
|
||||
NoExecutable: false,
|
||||
|
||||
@@ -288,7 +288,7 @@ func Test_toSyftFiles(t *testing.T) {
|
||||
},
|
||||
Executable: &file.Executable{
|
||||
Format: file.ELF,
|
||||
SecurityFeatures: &file.ELFSecurityFeatures{
|
||||
ELFSecurityFeatures: &file.ELFSecurityFeatures{
|
||||
SymbolTableStripped: false,
|
||||
StackCanary: boolRef(true),
|
||||
NoExecutable: false,
|
||||
@@ -329,7 +329,7 @@ func Test_toSyftFiles(t *testing.T) {
|
||||
Executables: map[file.Coordinates]file.Executable{
|
||||
coord: {
|
||||
Format: file.ELF,
|
||||
SecurityFeatures: &file.ELFSecurityFeatures{
|
||||
ELFSecurityFeatures: &file.ELFSecurityFeatures{
|
||||
SymbolTableStripped: false,
|
||||
StackCanary: boolRef(true),
|
||||
NoExecutable: false,
|
||||
|
||||
@@ -19,6 +19,7 @@ func AllTypes() []any {
|
||||
pkg.DotnetDepsEntry{},
|
||||
pkg.DotnetPortableExecutableEntry{},
|
||||
pkg.DpkgDBEntry{},
|
||||
pkg.ELFBinaryPackageNoteJSONPayload{},
|
||||
pkg.ElixirMixLockEntry{},
|
||||
pkg.ErlangRebarLockEntry{},
|
||||
pkg.GolangBinaryBuildinfoEntry{},
|
||||
|
||||
@@ -74,6 +74,7 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.DotnetDepsEntry{}, "dotnet-deps-entry", "DotnetDepsMetadata"),
|
||||
jsonNames(pkg.DotnetPortableExecutableEntry{}, "dotnet-portable-executable-entry"),
|
||||
jsonNames(pkg.DpkgDBEntry{}, "dpkg-db-entry", "DpkgMetadata"),
|
||||
jsonNames(pkg.ELFBinaryPackageNoteJSONPayload{}, "elf-binary-package-note-json-payload"),
|
||||
jsonNames(pkg.RubyGemspec{}, "ruby-gemspec", "GemMetadata"),
|
||||
jsonNames(pkg.GolangBinaryBuildinfoEntry{}, "go-module-buildinfo-entry", "GolangBinMetadata", "GolangMetadata"),
|
||||
jsonNames(pkg.GolangModuleEntry{}, "go-module-entry", "GolangModMetadata"),
|
||||
|
||||
@@ -12,3 +12,12 @@ type ClassifierMatch struct {
|
||||
Classifier string `mapstructure:"Classifier" json:"classifier"`
|
||||
Location file.Location `mapstructure:"Location" json:"location"`
|
||||
}
|
||||
|
||||
// ELFBinaryPackageNoteJSONPayload Represents metadata captured from the .note.package section of the binary
|
||||
type ELFBinaryPackageNoteJSONPayload struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Vendor string `json:"vendor,omitempty"`
|
||||
System string `json:"system,omitempty"`
|
||||
SourceRepo string `json:"sourceRepo,omitempty"`
|
||||
Commit string `json:"commit,omitempty"`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Adding tests for the Binary cataloger
|
||||
# Adding tests for the Binary classifier cataloger
|
||||
|
||||
> [!TIP]
|
||||
> **TL;DR** to add a test for a new classifier:
|
||||
|
||||
@@ -125,7 +125,7 @@ func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate stri
|
||||
|
||||
matchMetadata := internal.MatchNamedCaptureGroups(tmplPattern, string(contents))
|
||||
|
||||
p := newPackage(classifier, location, matchMetadata)
|
||||
p := newClassifierPackage(classifier, location, matchMetadata)
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func FileContentsVersionMatcher(pattern string) EvidenceMatcher {
|
||||
|
||||
matchMetadata := internal.MatchNamedCaptureGroups(pat, string(contents))
|
||||
|
||||
p := newPackage(classifier, location, matchMetadata)
|
||||
p := newClassifierPackage(classifier, location, matchMetadata)
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
var emptyPURL = packageurl.PackageURL{}
|
||||
|
||||
func newPackage(classifier Classifier, location file.Location, matchMetadata map[string]string) *pkg.Package {
|
||||
func newClassifierPackage(classifier Classifier, location file.Location, matchMetadata map[string]string) *pkg.Package {
|
||||
version, ok := matchMetadata["version"]
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -0,0 +1,35 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet, licenses []pkg.License) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: metadata.Name,
|
||||
Version: metadata.Version,
|
||||
Licenses: pkg.NewLicenseSet(licenses...),
|
||||
PURL: packageURL(metadata),
|
||||
Type: pkg.BinaryPkg,
|
||||
Locations: locations,
|
||||
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func packageURL(metadata elfBinaryPackageNotes) string {
|
||||
// TODO: what if the System value is not set?
|
||||
return packageurl.NewPackageURL(
|
||||
packageurl.TypeGeneric,
|
||||
metadata.System,
|
||||
metadata.Name,
|
||||
metadata.Version,
|
||||
nil,
|
||||
"",
|
||||
).ToString()
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"context"
|
||||
"debug/elf"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
var _ pkg.Cataloger = (*elfPackageCataloger)(nil)
|
||||
|
||||
type elfPackageCataloger struct {
|
||||
}
|
||||
|
||||
// TODO: for now this accounts for a single data shape from the .note.package section of an ELF binary.
|
||||
// In the future, this should be generalized to support multiple data shapes, including non-json data.
|
||||
// For example, fedora includes an ELF section header as a prefix to the JSON payload: https://github.com/anchore/syft/issues/2713
|
||||
|
||||
type elfBinaryPackageNotes struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
PURL string `json:"purl"`
|
||||
CPE string `json:"cpe"`
|
||||
License string `json:"license"`
|
||||
pkg.ELFBinaryPackageNoteJSONPayload `json:",inline"`
|
||||
Location file.Location `json:"-"`
|
||||
}
|
||||
|
||||
type elfPackageKey struct {
|
||||
Name string
|
||||
Version string
|
||||
PURL string
|
||||
CPE string
|
||||
}
|
||||
|
||||
func NewELFPackageCataloger() pkg.Cataloger {
|
||||
return &elfPackageCataloger{}
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) Name() string {
|
||||
return "elf-binary-package-cataloger"
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
locations, err := resolver.FilesByMIMEType(mimetype.ExecutableMIMETypeSet.List()...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get binary files by mime type: %w", err)
|
||||
}
|
||||
|
||||
// first find all ELF binaries that have notes
|
||||
var notesByLocation = make(map[elfPackageKey][]elfBinaryPackageNotes)
|
||||
for _, location := range locations {
|
||||
reader, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get binary contents %q: %w", location.Path(), err)
|
||||
}
|
||||
|
||||
notes, err := c.parseElfNotes(file.LocationReadCloser{
|
||||
Location: location,
|
||||
ReadCloser: reader,
|
||||
})
|
||||
if err != nil {
|
||||
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
|
||||
continue
|
||||
}
|
||||
|
||||
if notes == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
notes.Location = location
|
||||
key := elfPackageKey{
|
||||
Name: notes.Name,
|
||||
Version: notes.Version,
|
||||
PURL: notes.PURL,
|
||||
CPE: notes.CPE,
|
||||
}
|
||||
notesByLocation[key] = append(notesByLocation[key], *notes)
|
||||
}
|
||||
|
||||
// now we have all ELF binaries that have notes, let's create packages for them.
|
||||
// we do this in a second pass since it is possible that we have multiple ELF binaries with the same name and version
|
||||
// which means the set of binaries collectively represent a single logical package.
|
||||
var pkgs []pkg.Package
|
||||
for _, notes := range notesByLocation {
|
||||
noteLocations := file.NewLocationSet()
|
||||
for _, note := range notes {
|
||||
noteLocations.Add(note.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
}
|
||||
|
||||
// create a package for each unique name/version pair (based on the first note found)
|
||||
pkgs = append(pkgs, newELFPackage(notes[0], noteLocations, nil))
|
||||
}
|
||||
|
||||
// why not return relationships? We have an executable cataloger that will note the dynamic libraries imported by
|
||||
// each binary. After all files and packages are processed there is a final task that creates package-to-package
|
||||
// and package-to-file relationships based on the dynamic libraries imported by each binary.
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) parseElfNotes(reader file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
||||
metadata, err := getELFNotes(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to process ELF binary: %w", err)
|
||||
}
|
||||
|
||||
if metadata == nil || metadata.Name == "" || metadata.Version == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
||||
unionReader, err := unionreader.GetUnionReader(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get union reader for binary: %w", err)
|
||||
}
|
||||
|
||||
f, err := elf.NewFile(unionReader)
|
||||
if f == nil || err != nil {
|
||||
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to parse binary as ELF")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
noteSection := f.Section(".note.package")
|
||||
if noteSection == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
notes, err := noteSection.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var metadata elfBinaryPackageNotes
|
||||
if err := json.Unmarshal(notes, &metadata); err != nil {
|
||||
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &metadata, err
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
Name: "libhello_world.so",
|
||||
Version: "0.01",
|
||||
PURL: "pkg:generic/syftsys/libhello_world.so@0.01",
|
||||
FoundBy: "",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewVirtualLocation("/usr/local/bin/elftests/elfbinwithnestedlib/bin/lib/libhello_world.so", "/usr/local/bin/elftests/elfbinwithnestedlib/bin/lib/libhello_world.so"),
|
||||
file.NewVirtualLocation("/usr/local/bin/elftests/elfbinwithsisterlib/lib/libhello_world.so", "/usr/local/bin/elftests/elfbinwithsisterlib/lib/libhello_world.so"),
|
||||
file.NewVirtualLocation("/usr/local/bin/elftests/elfbinwithsisterlib/lib/libhello_world2.so", "/usr/local/bin/elftests/elfbinwithsisterlib/lib/libhello_world2.so"),
|
||||
),
|
||||
Language: "",
|
||||
Type: pkg.BinaryPkg,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "testfixture",
|
||||
Vendor: "syft",
|
||||
System: "syftsys",
|
||||
SourceRepo: "https://github.com/someone/somewhere.git",
|
||||
Commit: "5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "syfttestfixture",
|
||||
Version: "0.01",
|
||||
PURL: "pkg:generic/syftsys/syfttestfixture@0.01",
|
||||
FoundBy: "",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/usr/local/bin/elftests/elfbinwithnestedlib/bin/elfbinwithnestedlib").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
file.NewLocation("/usr/local/bin/elftests/elfbinwithsisterlib/bin/elfwithparallellibbin1").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
file.NewLocation("/usr/local/bin/elftests/elfbinwithsisterlib/bin/elfwithparallellibbin2").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Language: "",
|
||||
Type: pkg.BinaryPkg,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "testfixture",
|
||||
Vendor: "syft",
|
||||
System: "syftsys",
|
||||
SourceRepo: "https://github.com/someone/somewhere.git",
|
||||
Commit: "5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
WithImageResolver(t, "elf-test-fixtures").
|
||||
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
|
||||
Expects(expectedPkgs, nil).
|
||||
TestCataloger(t, NewELFPackageCataloger())
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_packageURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
notes elfBinaryPackageNotes
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "elf-binary-package-cataloger",
|
||||
notes: elfBinaryPackageNotes{
|
||||
Name: "github.com/anchore/syft",
|
||||
Version: "v0.1.0",
|
||||
ELFBinaryPackageNoteJSONPayload: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
System: "syftsys",
|
||||
},
|
||||
},
|
||||
expected: "pkg:generic/syftsys/github.com/anchore/syft@v0.1.0",
|
||||
},
|
||||
{
|
||||
name: "elf binary package short name",
|
||||
notes: elfBinaryPackageNotes{
|
||||
Name: "go.opencensus.io",
|
||||
Version: "v0.23.0",
|
||||
ELFBinaryPackageNoteJSONPayload: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
System: "syftsys",
|
||||
},
|
||||
},
|
||||
expected: "pkg:generic/syftsys/go.opencensus.io@v0.23.0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, packageURL(test.notes))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_newELFPackage(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
metadata elfBinaryPackageNotes
|
||||
expected pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "elf-binary-package-cataloger",
|
||||
metadata: elfBinaryPackageNotes{
|
||||
Name: "syfttestfixture",
|
||||
Version: "0.01",
|
||||
PURL: "pkg:generic/syftsys/syfttestfixture@0.01",
|
||||
CPE: "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01",
|
||||
ELFBinaryPackageNoteJSONPayload: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "binary",
|
||||
System: "syftsys",
|
||||
},
|
||||
},
|
||||
|
||||
expected: pkg.Package{
|
||||
Name: "syfttestfixture",
|
||||
Version: "0.01",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/syftsys/syfttestfixture@0.01",
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "binary",
|
||||
System: "syftsys",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := newELFPackage(test.metadata, file.NewLocationSet(), nil)
|
||||
if diff := cmp.Diff(test.expected, actual, cmpopts.IgnoreFields(pkg.Package{}, "id"), cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{})); diff != "" {
|
||||
t.Errorf("newELFPackage() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
FROM rockylinux:8
|
||||
RUN dnf update -y; \
|
||||
dnf install make automake gcc gcc-c++ kernel-devel -y; \
|
||||
dnf clean all
|
||||
RUN mkdir -p /usr/local/bin/elftests/elfbinwithnestedlib
|
||||
RUN mkdir -p /usr/local/bin/elftests/elfbinwithsisterlib
|
||||
COPY ./elfbinwithnestedlib /usr/local/bin/elftests/elfbinwithnestedlib
|
||||
COPY ./elfbinwithsisterlib /usr/local/bin/elftests/elfbinwithsisterlib
|
||||
ENV LD_LIBRARY_PATH=/usr/local/bin/elftests/elfbinwithnestedlib/bin/lib
|
||||
WORKDIR /usr/local/bin/elftests/elfbinwithnestedlib/
|
||||
RUN make
|
||||
WORKDIR /usr/local/bin/elftests/elfbinwithsisterlib
|
||||
RUN make
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#include <iostream>
|
||||
#include "hello_world.h"
|
||||
|
||||
void print_hello_world() {
|
||||
std::cout << "Hello, World!" << std::endl;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
#ifndef HELLO_WORLD_H
|
||||
#define HELLO_WORLD_H
|
||||
|
||||
// Function declaration for printing "Hello, World!" to stdout
|
||||
void print_hello_world();
|
||||
|
||||
#endif // HELLO_WORLD_H
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
LDFLAGS := -L/lib64 -lstdc++
|
||||
|
||||
SRC_DIR := ./
|
||||
BUILD_DIR := ../build
|
||||
BIN_DIR := ../bin
|
||||
LIB_DIR := $(BIN_DIR)/lib
|
||||
|
||||
LIB_NAME := hello_world
|
||||
LIB_SRC := $(SRC_DIR)/hello_world.cpp
|
||||
LIB_OBJ := $(BUILD_DIR)/$(LIB_NAME).o
|
||||
LIB_SO := $(LIB_DIR)/lib$(LIB_NAME).so
|
||||
|
||||
EXECUTABLE := elfbinwithnestedlib
|
||||
EXEC_SRC := $(SRC_DIR)/testbin.cpp
|
||||
EXEC_OBJ := $(BUILD_DIR)/$(EXECUTABLE).o
|
||||
|
||||
|
||||
|
||||
all: testfixture
|
||||
|
||||
$(LIB_SO): $(LIB_OBJ) | $(LIB_DIR)
|
||||
$(CC) -shared -o $@ $<
|
||||
echo '{"type": "testfixture","license":"MIT","commit":"5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb","sourceRepo":"https://github.com/someone/somewhere.git","vendor": "syft","system": "syftsys","name": "libhello_world.so","version": "0.01","purl": "pkg:generic/syftsys/syfttestfixture@0.01","cpe": "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
$(LIB_OBJ): $(LIB_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -fPIC -c $< -o $@
|
||||
|
||||
$(EXEC_OBJ): $(EXEC_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BIN_DIR):
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(LIB_DIR):
|
||||
mkdir -p $(LIB_DIR)
|
||||
|
||||
$(BIN_DIR)/$(EXECUTABLE): $(EXEC_OBJ) $(LIB_SO) | $(BIN_DIR)
|
||||
$(CC) $(CFLAGS) -o $@ $^ -L$(LIB_DIR) -l$(LIB_NAME) $(LDFLAGS)
|
||||
echo '{"type": "testfixture","license":"MIT","commit":"5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb","sourceRepo":"https://github.com/someone/somewhere.git","vendor": "syft","system": "syftsys","name": "syfttestfixture","version": "0.01","purl": "pkg:generic/syftsys/syfttestfixture@0.01","cpe": "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
testfixture: $(BIN_DIR)/$(EXECUTABLE)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(LIB_DIR) $(BIN_DIR) $(EXECUTABLE)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#include "hello_world.h"
|
||||
|
||||
int main() {
|
||||
// Call the function from the shared library
|
||||
print_hello_world();
|
||||
|
||||
return 0;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
CC = g++
|
||||
CFLAGS = -std=c++17 -Wall -Wextra -pedantic
|
||||
BUILD_DIR := ./build
|
||||
BIN_DIR := ./bin
|
||||
LIB_DIR := $(BIN_DIR)/lib
|
||||
|
||||
|
||||
|
||||
all: testfixtures
|
||||
|
||||
testfixtures:
|
||||
$(MAKE) -C elfsrc
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(BIN_DIR)
|
||||
|
||||
.PHONY: all clean testfixtures
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#include <iostream>
|
||||
#include "hello_world.h"
|
||||
|
||||
void print_hello_world() {
|
||||
std::cout << "Hello, World!" << std::endl;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
#ifndef HELLO_WORLD_H
|
||||
#define HELLO_WORLD_H
|
||||
|
||||
// Function declaration for printing "Hello, World!" to stdout
|
||||
void print_hello_world();
|
||||
|
||||
#endif // HELLO_WORLD_H
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
LDFLAGS := -L/lib64 -lstdc++
|
||||
|
||||
SRC_DIR := ./
|
||||
BUILD_DIR := ../build
|
||||
LIB_DIR := ../lib
|
||||
BIN_DIR := ../bin
|
||||
|
||||
LIB_NAME := hello_world
|
||||
LIB_SRC := $(SRC_DIR)/hello_world.cpp
|
||||
LIB_OBJ := $(BUILD_DIR)/$(LIB_NAME).o
|
||||
LIB_SO := $(LIB_DIR)/lib$(LIB_NAME).so
|
||||
|
||||
EXECUTABLE := elfwithparallellibbin1
|
||||
EXEC_SRC := $(SRC_DIR)/testbin.cpp
|
||||
EXEC_OBJ := $(BUILD_DIR)/$(EXECUTABLE).o
|
||||
|
||||
|
||||
|
||||
all: testfixture
|
||||
|
||||
$(LIB_SO): $(LIB_OBJ) | $(LIB_DIR)
|
||||
$(CC) -shared -o $@ $<
|
||||
echo '{"type": "testfixture","license":"MIT","commit":"5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb","sourceRepo":"https://github.com/someone/somewhere.git","vendor": "syft","system": "syftsys","name": "libhello_world.so","version": "0.01","purl": "pkg:generic/syftsys/syfttestfixture@0.01","cpe": "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
$(LIB_OBJ): $(LIB_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -fPIC -c $< -o $@
|
||||
|
||||
$(EXEC_OBJ): $(EXEC_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BIN_DIR):
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(LIB_DIR):
|
||||
mkdir -p $(LIB_DIR)
|
||||
|
||||
$(BIN_DIR)/$(EXECUTABLE): $(EXEC_OBJ) $(LIB_SO) | $(BIN_DIR)
|
||||
$(CC) $(CFLAGS) -o $@ $^ -L$(LIB_DIR) -l$(LIB_NAME) $(LDFLAGS)
|
||||
echo '{"type": "testfixture","license":"MIT","commit":"5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb","sourceRepo":"https://github.com/someone/somewhere.git","vendor": "syft","system": "syftsys","name": "syfttestfixture","version": "0.01","purl": "pkg:generic/syftsys/syfttestfixture@0.01","cpe": "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
testfixture: $(BIN_DIR)/$(EXECUTABLE)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(LIB_DIR) $(BIN_DIR) $(EXECUTABLE)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#include "hello_world.h"
|
||||
|
||||
int main() {
|
||||
// Call the function from the shared library
|
||||
print_hello_world();
|
||||
|
||||
return 0;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#include <iostream>
|
||||
#include "hello_world2.h"
|
||||
|
||||
void print_hello_world() {
|
||||
std::cout << "Hello, World again!!" << std::endl;
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
|
||||
#ifndef HELLO_WORLD2_H
|
||||
#define HELLO_WORLD2_H
|
||||
|
||||
// Function declaration for printing "Hello, World!" to stdout
|
||||
void print_hello_world();
|
||||
|
||||
#endif // HELLO_WORLD2_H
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
LDFLAGS := -L/lib64 -lstdc++
|
||||
|
||||
SRC_DIR := ./
|
||||
BUILD_DIR := ../build
|
||||
LIB_DIR := ../lib
|
||||
BIN_DIR := ../bin
|
||||
|
||||
LIB_NAME := hello_world2
|
||||
LIB_SRC := $(SRC_DIR)/hello_world2.cpp
|
||||
LIB_OBJ := $(BUILD_DIR)/$(LIB_NAME).o
|
||||
LIB_SO := $(LIB_DIR)/lib$(LIB_NAME).so
|
||||
|
||||
EXECUTABLE := elfwithparallellibbin2
|
||||
EXEC_SRC := $(SRC_DIR)/testbin2.cpp
|
||||
EXEC_OBJ := $(BUILD_DIR)/$(EXECUTABLE).o
|
||||
|
||||
|
||||
|
||||
all: testfixture
|
||||
|
||||
$(LIB_SO): $(LIB_OBJ) | $(LIB_DIR)
|
||||
$(CC) -shared -o $@ $<
|
||||
echo '{"type": "testfixture","license":"MIT","commit":"5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb","sourceRepo":"https://github.com/someone/somewhere.git","vendor": "syft","system": "syftsys","name": "libhello_world.so","version": "0.01","purl": "pkg:generic/syftsys/syfttestfixture@0.01","cpe": "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
$(LIB_OBJ): $(LIB_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -fPIC -c $< -o $@
|
||||
|
||||
$(EXEC_OBJ): $(EXEC_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BIN_DIR):
|
||||
mkdir -p $(BIN_DIR)
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
$(LIB_DIR):
|
||||
mkdir -p $(LIB_DIR)
|
||||
|
||||
$(BIN_DIR)/$(EXECUTABLE): $(EXEC_OBJ) $(LIB_SO) | $(BIN_DIR)
|
||||
$(CC) $(CFLAGS) -o $@ $^ -L$(LIB_DIR) -l$(LIB_NAME) $(LDFLAGS)
|
||||
echo '{"type": "testfixture","license":"MIT","commit":"5534c38d0ffef9a3f83154f0b7a7fb6ab0ab6dbb","sourceRepo":"https://github.com/someone/somewhere.git","vendor": "syft","system": "syftsys","name": "syfttestfixture","version": "0.01","purl": "pkg:generic/syftsys/syfttestfixture@0.01","cpe": "cpe:/o:syft:syftsys_testfixture_syfttestfixture:0.01"}' | objcopy --add-section .note.package=/dev/stdin --set-section-flags .note.package=noload,readonly $@
|
||||
|
||||
testfixture: $(BIN_DIR)/$(EXECUTABLE)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(LIB_DIR) $(BIN_DIR) $(EXECUTABLE)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#include "hello_world2.h"
|
||||
|
||||
int main() {
|
||||
// Call the function from the shared library
|
||||
print_hello_world();
|
||||
|
||||
return 0;
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
CC = g++
|
||||
CFLAGS = -std=c++17 -Wall -Wextra -pedantic
|
||||
BUILD_DIR := ./build
|
||||
LIB_DIR := ./lib
|
||||
BIN_DIR := ./bin
|
||||
|
||||
|
||||
all: testfixtures
|
||||
|
||||
testfixtures:
|
||||
$(MAKE) -C elfsrc1
|
||||
$(MAKE) -C elfsrc2
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(LIB_DIR) $(BIN_DIR)
|
||||
|
||||
.PHONY: all clean testfixtures
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package dotnet
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_getDepsJSONFilePrefix(t *testing.T) {
|
||||
|
||||
@@ -168,7 +168,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
f.TargetMachine = magic
|
||||
|
||||
// Read XCOFF file header
|
||||
if _, err := sr.Seek(0, os.SEEK_SET); err != nil {
|
||||
if _, err := sr.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var nscns uint16
|
||||
@@ -205,7 +205,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
|
||||
// Read string table (located right after symbol table).
|
||||
offset := symptr + uint64(nsyms)*SYMESZ
|
||||
if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
|
||||
if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The first 4 bytes contain the length (in bytes).
|
||||
@@ -214,7 +214,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
return nil, err
|
||||
}
|
||||
if l > 4 {
|
||||
if _, err := sr.Seek(int64(offset), os.SEEK_SET); err != nil {
|
||||
if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.StringTable = make([]byte, l)
|
||||
@@ -224,7 +224,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
}
|
||||
|
||||
// Read section headers
|
||||
if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), os.SEEK_SET); err != nil {
|
||||
if _, err := sr.Seek(int64(hdrsz)+int64(opthdr), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Sections = make([]*Section, nscns)
|
||||
@@ -270,7 +270,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
var idxToSym = make(map[int]*Symbol)
|
||||
|
||||
// Read symbol table
|
||||
if _, err := sr.Seek(int64(symptr), os.SEEK_SET); err != nil {
|
||||
if _, err := sr.Seek(int64(symptr), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Symbols = make([]*Symbol, 0)
|
||||
@@ -356,7 +356,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
|
||||
// Read csect auxiliary entry (by convention, it is the last).
|
||||
if !needAuxFcn {
|
||||
if _, err := sr.Seek(int64(numaux-1)*SYMESZ, os.SEEK_CUR); err != nil {
|
||||
if _, err := sr.Seek(int64(numaux-1)*SYMESZ, io.SeekCurrent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -383,7 +383,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
f.Symbols = append(f.Symbols, sym)
|
||||
skip:
|
||||
i += numaux // Skip auxiliary entries
|
||||
if _, err := sr.Seek(int64(numaux)*SYMESZ, os.SEEK_CUR); err != nil {
|
||||
if _, err := sr.Seek(int64(numaux)*SYMESZ, io.SeekCurrent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -398,7 +398,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
|
||||
if sect.Relptr == 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := sr.Seek(int64(sect.Relptr), os.SEEK_SET); err != nil {
|
||||
if _, err := sr.Seek(int64(sect.Relptr), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := uint32(0); i < sect.Nreloc; i++ {
|
||||
@@ -509,7 +509,7 @@ func (s *Section) Data() ([]byte, error) {
|
||||
// Library name pattern is either path/base/member or base/member
|
||||
func (f *File) readImportIDs(s *Section) ([]string, error) {
|
||||
// Read loader header
|
||||
if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
|
||||
if _, err := s.sr.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var istlen uint32
|
||||
@@ -535,7 +535,7 @@ func (f *File) readImportIDs(s *Section) ([]string, error) {
|
||||
}
|
||||
|
||||
// Read loader import file ID table
|
||||
if _, err := s.sr.Seek(int64(impoff), os.SEEK_SET); err != nil {
|
||||
if _, err := s.sr.Seek(int64(impoff), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
table := make([]byte, istlen)
|
||||
@@ -578,7 +578,7 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
|
||||
return nil, nil
|
||||
}
|
||||
// Read loader header
|
||||
if _, err := s.sr.Seek(0, os.SEEK_SET); err != nil {
|
||||
if _, err := s.sr.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var stlen uint32
|
||||
@@ -607,7 +607,7 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
|
||||
}
|
||||
|
||||
// Read loader section string table
|
||||
if _, err := s.sr.Seek(int64(stoff), os.SEEK_SET); err != nil {
|
||||
if _, err := s.sr.Seek(int64(stoff), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := make([]byte, stlen)
|
||||
@@ -622,7 +622,7 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
|
||||
}
|
||||
|
||||
// Read loader symbol table
|
||||
if _, err := s.sr.Seek(int64(symoff), os.SEEK_SET); err != nil {
|
||||
if _, err := s.sr.Seek(int64(symoff), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all := make([]ImportedSymbol, 0)
|
||||
|
||||
@@ -82,7 +82,7 @@ func LanguageByName(name string) Language {
|
||||
return Dart
|
||||
case string(Dotnet), ".net", packageurl.TypeNuget:
|
||||
return Dotnet
|
||||
case packageurl.TypeCocoapods, packageurl.TypeSwift, string(CocoapodsPkg), string(SwiftPkg):
|
||||
case packageurl.TypeCocoapods, packageurl.TypeSwift, string(CocoapodsPkg):
|
||||
return Swift
|
||||
case packageurl.TypeConan, string(CPP):
|
||||
return CPP
|
||||
|
||||
Reference in New Issue
Block a user