diff --git a/.github/actions/install-yara/action.yml b/.github/actions/install-yara/action.yml new file mode 100644 index 0000000..8de9b5a --- /dev/null +++ b/.github/actions/install-yara/action.yml @@ -0,0 +1,48 @@ +name: 'Install libyara' +description: 'Installs libyara and its dependencies' +inputs: + yara-version: + description: "Yara version" + required: true + cache: + description: "Activates caching if set to 'true'" + default: 'false' + required: false + skip-install: + description: "Yara is only built, not installed if 'true'" + default: 'false' + required: false +runs: + using: "composite" + steps: + - name: Install libyara dependencies + run: sudo apt-get update && sudo apt-get install -y libssl-dev libprotobuf-dev flex bison + shell: bash + + - name: Create dependencies directory + run: mkdir -p deps + shell: bash + + - name: Cache libyara + uses: actions/cache@v3 + id: yara-cache + with: + path: deps + key: ${{ runner.os }}-yara-${{ inputs.yara-version }} + if: ${{ inputs.cache == 'true' }} + + - name: Build libyara + run: | + git clone --depth=1 --branch ${{ inputs.yara-version }} https://github.com/VirusTotal/yara.git deps/yara + cd deps/yara + ./bootstrap.sh + ./configure --prefix=/usr --with-crypto || exit $? + make -j + shell: bash + if: ${{ inputs.cache != 'true' || steps.yara-cache.outputs.cache-hit != 'true' }} + + - name: Install libyara + run: sudo make install + shell: bash + working-directory: deps/yara + if: ${{ inputs.skip-install != 'true' }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 064a501..ad3857f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,79 +2,48 @@ name: CI on: push: - branches: - - master - - develop + branches: [ master , develop ] pull_request: - branches: - - master - - develop + branches: [ master , develop ] jobs: setup: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest strategy: matrix: - yara-version: - - v4.2.1 + yara-version: [ "v4.2.1" ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Install libyara dependencies - run: sudo apt-get update && sudo apt-get install -y libssl-dev libprotobuf-dev flex bison - - name: Cache libyara - uses: actions/cache@v2 - id: yara-cache + - name: Build yara + uses: ./.github/actions/install-yara with: - key: ${{ runner.os }}-yara-${{ matrix.yara-version }} - path: deps - - run: mkdir -p deps - - name: Build libyara - run: | - git clone --depth=1 --branch ${{ matrix.yara-version }} https://github.com/VirusTotal/yara.git deps/yara - cd deps/yara - ./bootstrap.sh - ./configure --prefix=/usr --with-crypto || exit $? - make -j - if: steps.yara-cache.outputs.cache-hit != 'true' + yara-version: ${{ matrix.yara-version }} + cache: 'true' + skip-install: 'true' test: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest needs: setup strategy: matrix: - go-version: - - 1.18 - yara-version: - - v4.2.1 + go-version: [ "1.18" ] + yara-version: [ "v4.2.1" ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Install libyara dependencies - run: sudo apt-get update && sudo apt-get install -y libssl-dev libprotobuf-dev flex bison - - name: Cache libyara - uses: actions/cache@v2 - id: yara-cache + - name: Install dependencies + uses: ./.github/actions/install-yara with: - key: ${{ runner.os }}-yara-${{ matrix.yara-version }} - path: deps - - run: mkdir -p deps - - name: Build libyara - run: | - git clone --depth=1 --branch ${{ matrix.yara-version }} https://github.com/VirusTotal/yara.git deps/yara - cd deps/yara - ./bootstrap.sh - ./configure --prefix=/usr --with-crypto || exit $? - make -j - if: steps.yara-cache.outputs.cache-hit != 'true' - - name: Install libyara - run: sudo make install - working-directory: deps/yara + yara-version: ${{ matrix.yara-version }} + cache: 'true' - name: Set up Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} + cache: true + cache-dependency-path: go.sum - name: Running unit tests run: go test -race -coverprofile=coverage.txt -covermode=atomic -coverpkg="$coverpkg" -v $(go list ./... | grep -v /acceptanceTests) @@ -88,60 +57,44 @@ jobs: - name: Consolidating coverage run: cat acceptanceTests/coverage.txt | tail -n+2 >> coverage.txt && rm acceptanceTests/coverage.txt - name: Upload coverage - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: coverage-linux-${{ matrix.go-version }}-${{ matrix.yara-version }} path: coverage.txt - name: Codecov - uses: codecov/codecov-action@v1.5.2 + uses: codecov/codecov-action@v3 build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest needs: setup strategy: matrix: - go-version: - - 1.18 - yara-version: - - v4.2.1 - openssl-version: - - OpenSSL_1_1_1-stable + go-version: [ "1.18" ] + yara-version: [ "v4.2.1" ] + openssl-version: [ "OpenSSL_1_1_1-stable" ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Set up Go - uses: actions/setup-go@v2 + - name: Install dependencies + uses: ./.github/actions/install-yara with: - go-version: ${{ matrix.go-version }} + yara-version: ${{ matrix.yara-version }} + cache: 'true' - - name: Install libyara dependencies - run: sudo apt-get update && sudo apt-get install -y libssl-dev libprotobuf-dev flex bison - - name: Cache libyara - uses: actions/cache@v2 - id: yara-cache + - name: Set up Go + uses: actions/setup-go@v3 with: - key: ${{ runner.os }}-yara-${{ matrix.yara-version }} - path: deps - - run: mkdir -p deps - - name: Build libyara - run: | - git clone --depth=1 --branch ${{ matrix.yara-version }} https://github.com/VirusTotal/yara.git deps/yara - cd deps/yara - ./bootstrap.sh - ./configure --prefix=/usr --with-crypto || exit $? - make -j - if: steps.yara-cache.outputs.cache-hit != 'true' - - name: Install libyara - run: sudo make install - working-directory: deps/yara + go-version: ${{ matrix.go-version }} + cache: true + cache-dependency-path: go.sum - run: mkdir -p cicd/build/ - name: Building yapscan for linux run: go build -trimpath -o ../../cicd/build/yapscan working-directory: cmd/yapscan - name: Upload linux build - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: yapscan-linux path: cicd/build/yapscan @@ -149,7 +102,7 @@ jobs: - name: Gather linux deps uses: ./.github/actions/gather-linux-deps - name: Upload linux deps - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: deps-linux path: | @@ -158,7 +111,7 @@ jobs: cicd/build/libyara.license - name: Docker Login - uses: docker/login-action@v1.10.0 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} @@ -172,7 +125,7 @@ jobs: openssl-version: ${{ matrix.openssl-version }} yara-version: ${{ matrix.yara-version }} - name: Upload windows build - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: yapscan-windows path: | diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..8529b2b --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,78 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master, develop ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master, develop ] + schedule: + - cron: '40 14 * * 0' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + yara-version: [ 'v4.2.1' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install dependencies + uses: ./.github/actions/install-yara + with: + yara-version: ${{ matrix.yara-version }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0d57670..0000000 --- a/.travis.yml +++ /dev/null @@ -1,77 +0,0 @@ -language: go - -os: linux -dist: xenial - -go: - - "1.x" - - "1.15" - -env: - - OPENSSL_VERSION=OpenSSL_1_1_1-stable YARA_VERSION=v4.1.1 - -services: - - docker - -before_install: - - echo $TRAVIS_GO_VERSION - - sudo apt-get update - - sudo apt-get install -y libssl-dev libprotobuf-dev flex bison p7zip-full # libyara-dev package is too old - - mkdir deps - - git clone --depth=1 --branch $YARA_VERSION https://github.com/VirusTotal/yara.git deps/yara - - pushd deps/yara - - ./bootstrap.sh - - ./configure --prefix=/usr --with-crypto || exit $? - - make -j${BUILD_THREADS:-1} || exit $? - - sudo make install || exit $? - - popd - - pkg-config --cflags --libs yara - -install: - - go mod vendor - -script: - - coverpkg="github.com/fkie-cad/yapscan/..." - - go test -race -coverprofile=coverage.txt -covermode=atomic -coverpkg="$coverpkg" -v $(go list ./... | grep -v /acceptanceTests) - - pushd acceptanceTests - - go test -race -coverprofile=coverage.txt -covermode=atomic -coverpkg="$coverpkg" -v -quickchecks=${QUICKCHECKS:-100} -short . - - popd - - cat acceptanceTests/coverage.txt | tail -n+2 >> coverage.txt - - rm acceptanceTests/coverage.txt - - mkdir -p build/ &>/dev/null - - pushd cmd/yapscan - - go build -trimpath -o ../../cicd/build/yapscan - - popd - - pushd cicd/ - - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - - ./crossBuildForWindows.sh - - popd - -after_success: - - bash <(curl -s https://codecov.io/bash) - -before_deploy: - - pushd cicd/build - - ldd yapscan - - cp $(ldd yapscan | grep libyara | cut -d'>' -f2 | cut -d' ' -f2) . - - ldd libyara.so* - - cp $(ldd libyara.so* | grep libcrypto | cut -d'>' -f2 | cut -d' ' -f2) . - - 7z a yapscan_windows_amd64.zip yapscan.exe yapscan.dll - - 7z a yapscan_linux_amd64.zip yapscan libyara.so* libcrypto.so* - - tar -cvzf yapscan_windows_amd64.tar.gz yapscan.exe yapscan.dll - - tar -cvzf yapscan_linux_amd64.tar.gz yapscan libyara.so* libcrypto.so* - - popd - -deploy: - provider: releases - token: - secure: g36k1ZMwK/LWSxsM22sz9h//vOAnil/5mHc28te6HsoXold37Gk/OUC8OdVg0Ff3mbTgF0cL+aWWDVL1w+9z5zQJ6w2eHO8ONX7yzv3x70nWuznoN8x00sYGZ9vnVeGSMFPLIK8XJVvwW7F3cKNkrX2GaJU5E9uWfiyrPi0SxgeWvSveMwyCfwFp7kgYv1NzSpoXQxzozfFltFyBfKYzRxGm4rjf7gcxw6/DF+YsD7odQ6s6lxwLAvILYPM3DKnJaZ2ZFpknLFgl0a43seUgSWZtOqkssvZIrPyGLmorGjbHgmNBknhlRQZDAk0oiZGaLD3pgWCLtRzR+N8QkQ9uRSTuxMnIfWAgTvM8LSA+OJH+13IykwdVZxbEqqtie4Q9EZjjBJ3ikWuEuxnO4jfkQYrB1cgMyuJo3T/Ovh2fbuvERq70asFj+ooIYcVNBEY9bP8de+sobbE93xIQS5GatlAwYn2BDmYQGAkEe3DuOY3LrSt88UaxW5ObWOROHGSv5BWTwuIyhzPjorDQhI2gje+XGtdxdX3K4cMYnWP0+f9H0TscdWNK6Hbde/NUJIYrMpdZJMBi7Rp6yH0klDrLPYKRuWDsa26mSnW4cfXUkxW7WIMCFAaBAeoM0yyipJ4da/2JxLA/7+sgLfntD7CXw+62DS6gP8+dD1XT8hw1eM8= - file: - - cicd/build/yapscan_windows_amd64.zip - - cicd/build/yapscan_windows_amd64.tar.gz - - cicd/build/yapscan_linux_amd64.zip - - cicd/build/yapscan_linux_amd64.tar.gz - on: - repo: fkie-cad/yapscan - tags: true - condition: $TRAVIS_GO_VERSION =~ ^1\.x diff --git a/app/crash.go b/app/crash.go index 160c30f..d71d737 100644 --- a/app/crash.go +++ b/app/crash.go @@ -18,11 +18,10 @@ func crashProcess(c *cli.Context) error { if c.NArg() != 1 { return errors.Newf("expected exactly one arguments, got %d", c.NArg()) } - pid_, err := strconv.ParseUint(c.Args().Get(0), 10, 64) + pid, err := strconv.Atoi(c.Args().Get(0)) if err != nil { return errors.Newf("\"%s\" is not a pid", c.Args().Get(0)) } - pid := int(pid_) crashMethod, err := procio.ParseCrashMethod(c.String("method")) if err != nil { diff --git a/app/dump.go b/app/dump.go index 331bd40..64e742b 100644 --- a/app/dump.go +++ b/app/dump.go @@ -35,11 +35,10 @@ func dumpMemory(c *cli.Context) error { if c.NArg() != 1 && c.NArg() != 2 { return errors.Newf("expected exactly one or two arguments, got %d", c.NArg()) } - pid_, err := strconv.ParseUint(c.Args().Get(0), 10, 64) + pid, err := strconv.Atoi(c.Args().Get(0)) if err != nil { return errors.Newf("\"%s\" is not a pid", c.Args().Get(0)) } - pid := int(pid_) var addr uintptr allSegments := c.NArg() < 2 diff --git a/app/listMem.go b/app/listMem.go index 08d1376..c17d66e 100644 --- a/app/listMem.go +++ b/app/listMem.go @@ -19,11 +19,10 @@ func listMemory(c *cli.Context) error { if c.NArg() != 1 { return errors.Newf("expected exactly one argument, got %d", c.NArg()) } - pid_, err := strconv.ParseUint(c.Args().Get(0), 10, 64) + pid, err := strconv.Atoi(c.Args().Get(0)) if err != nil { return errors.Newf("\"%s\" is not a pid", c.Args().Get(0)) } - pid := int(pid_) f, err := filterFromArgs(c) if err != nil { diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..5594447 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +coverage: + precision: 2 + round: down + range: "50...75" + +ignore: + - acceptanceTests + - testutil + - service # Service is only relevant for windows, but coverage is only created on linux diff --git a/experiments/listSegmentsOfProcess/listSegmentsOfProcess.go b/experiments/listSegmentsOfProcess/listSegmentsOfProcess.go deleted file mode 100644 index 7481755..0000000 --- a/experiments/listSegmentsOfProcess/listSegmentsOfProcess.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strconv" - - "github.com/dustin/go-humanize" - "github.com/fkie-cad/yapscan/procio" -) - -func main() { - if len(os.Args) != 2 && len(os.Args) != 3 { - fmt.Printf("Usage: %s [all]\n", os.Args[0]) - os.Exit(1) - } - - pid, err := strconv.Atoi(os.Args[1]) - if err != nil { - panic(err) - } - - all := false - if len(os.Args) == 3 { - all = true - } - - fmt.Printf("Reading segments from process %d...\n", pid) - proc, err := procio.OpenProcess(pid) - if err != nil { - panic(err) - } - defer proc.Close() - segments, err := proc.MemorySegments() - for _, seg := range segments { - fmt.Printf("0x%016x : %8s : %7v : %7v : %v : %v\n", seg.BaseAddress, humanize.Bytes(uint64(seg.Size)), seg.Type, seg.State, seg.AllocatedPermissions, seg.CurrentPermissions) - if all { - for _, subSeg := range seg.SubSegments { - fmt.Printf(" 0x%016x : %8s : %7v : %7v : %v : %v\n", subSeg.BaseAddress, humanize.Bytes(uint64(subSeg.Size)), subSeg.Type, subSeg.State, subSeg.AllocatedPermissions, subSeg.CurrentPermissions) - } - } - } - if err != nil { - panic(err) - } -} diff --git a/experiments/readFirstMemoryOfProc/readFirstMemoryOfProc.go b/experiments/readFirstMemoryOfProc/readFirstMemoryOfProc.go deleted file mode 100644 index ac9978d..0000000 --- a/experiments/readFirstMemoryOfProc/readFirstMemoryOfProc.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "encoding/hex" - "fmt" - "io" - "os" - "strconv" - - "github.com/fkie-cad/yapscan/procio" -) - -func main() { - if len(os.Args) != 3 { - fmt.Printf("Usage: %s \n", os.Args[0]) - os.Exit(1) - } - - pid, err := strconv.Atoi(os.Args[1]) - if err != nil { - panic(err) - } - - length, err := strconv.Atoi(os.Args[2]) - if err != nil { - panic(err) - } - - fmt.Printf("Reading segments from process %d...\n", pid) - proc, err := procio.OpenProcess(pid) - if err != nil { - panic(err) - } - defer proc.Close() - segments, err := proc.MemorySegments() - if err != nil { - panic(err) - } - - readSeg := segments[0] - - rdr, _ := procio.NewMemoryReader(proc, readSeg) - defer rdr.Close() - - if length > 0 { - buffer := make([]byte, length) - _, err = io.ReadFull(rdr, buffer) - if err != nil { - panic(err) - } - - fmt.Printf("Read at 0x%016x:\n", readSeg.BaseAddress) - fmt.Printf("%s", hex.Dump(buffer)) - } else { - dumper := hex.Dumper(os.Stdout) - defer dumper.Close() - - fmt.Printf("Read at 0x%016x:\n", readSeg.BaseAddress) - _, err := io.Copy(dumper, rdr) - if err != nil { - panic(err) - } - } -} diff --git a/go.mod b/go.mod index b0d7f26..e7e2446 100644 --- a/go.mod +++ b/go.mod @@ -7,17 +7,17 @@ require ( github.com/fatih/color v1.13.0 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/hillu/go-yara/v4 v4.2.3 - github.com/klauspost/compress v1.15.4 + github.com/klauspost/compress v1.15.5 github.com/rjNemo/underscore v0.3.1-0.20220323145844-235b36b7f6e8 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 github.com/sirupsen/logrus v1.8.1 github.com/smartystreets/goconvey v1.7.2 github.com/stretchr/testify v1.7.0 github.com/targodan/go-errors v1.0.0 - github.com/urfave/cli/v2 v2.6.0 + github.com/urfave/cli/v2 v2.8.1 github.com/yeka/zip v0.0.0-20180914125537-d046722c6feb - golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 - golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a ) require ( @@ -32,7 +32,8 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/smartystreets/assertions v1.13.0 // indirect github.com/stretchr/objx v0.3.0 // indirect - golang.org/x/exp v0.0.0-20220516143420-24438e51023a // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 11c7756..5d14f78 100644 --- a/go.sum +++ b/go.sum @@ -1,394 +1,87 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= -github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20210503212227-fb464eba2686 h1:M8mGEEKe5MUkENNKwreWXhiF0X9vH93ur4nmuUc6kT8= -github.com/gopherjs/gopherjs v0.0.0-20210503212227-fb464eba2686/go.mod h1:Opf9rtYVq0eTyX+aRVmRO9hE8ERAozcdrBxWG9Q6mkQ= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hillu/go-yara/v4 v4.2.3 h1:Gazusex0rkL8NCMjc2vXqKBIHZ4kiBlLdMt2DuvhL4A= github.com/hillu/go-yara/v4 v4.2.3/go.mod h1:AHEs/FXVMQKVVlT6iG9d+q1BRr0gq0WoAWZQaZ0gS7s= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.4 h1:1kn4/7MepF/CHmYub99/nNX8az0IJjfSOU/jbnTVfqQ= -github.com/klauspost/compress v1.15.4/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/klauspost/compress v1.15.5 h1:qyCLMz2JCrKADihKOh9FxnW3houKeNsp2h5OEz0QSEA= +github.com/klauspost/compress v1.15.5/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rjNemo/underscore v0.3.1-0.20220323145844-235b36b7f6e8 h1:4gIyk0JDuOJaKQyPWv9wXL0F128xIMjw4c0z61yAnxE= github.com/rjNemo/underscore v0.3.1-0.20220323145844-235b36b7f6e8/go.mod h1:y3LuKy2UP6zp7yZff5ZGRm1s/s9QvCoCoQZVqAkk3hM= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE= github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/targodan/go-errors v1.0.0 h1:H1hZke3MN9+Z06n1l4O0dYsC5Sm2d3W4ZcIJjQDiKlg= github.com/targodan/go-errors v1.0.0/go.mod h1:xF0Z1lpYQlz9suJZl6dXny+ZeDuJer0F8HiuVqaYkh4= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/urfave/cli/v2 v2.6.0 h1:yj2Drkflh8X/zUrkWlWlUjZYHyWN7WMmpVxyxXIUyv8= -github.com/urfave/cli/v2 v2.6.0/go.mod h1:oDzoM7pVwz6wHn5ogWgFUU1s4VJayeQS+aEZDqXIEJs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= +github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yeka/zip v0.0.0-20180914125537-d046722c6feb h1:OJYP70YMddlmGq//EPLj8Vw2uJXmrA+cGSPhXTDpn2E= github.com/yeka/zip v0.0.0-20180914125537-d046722c6feb/go.mod h1:9BnoKCcgJ/+SLhfAXj15352hTOuVmG5Gzo8xNRINfqI= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= -golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898 h1:SLP7Q4Di66FONjDJbCYrCRrh97focO6sLogHO7/g8F0= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7 h1:jynE66seADJbyWMUdeOyVTvPtBZt7L6LJHupGwxPZRM= -golang.org/x/exp v0.0.0-20220314205449-43aec2f8a4e7/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/exp v0.0.0-20220516143420-24438e51023a h1:tiLLxEjKNE6Hrah/Dp/cyHvsyjDLcMFSocOHO5XDmOM= -golang.org/x/exp v0.0.0-20220516143420-24438e51023a/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHLipW6hXmL3qpUixS2jw= +golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM= -golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/procio/memory_linux.go b/procio/memory_linux.go index 1c0d8f7..39b317c 100644 --- a/procio/memory_linux.go +++ b/procio/memory_linux.go @@ -7,6 +7,7 @@ import ( "bufio" "fmt" "io" + "os" "regexp" "strconv" "strings" @@ -28,6 +29,13 @@ const ( expectedFieldCount ) +const ( + nativeProtNone = C.PROT_NONE + nativeProtRead = C.PROT_READ + nativeProtWrite = C.PROT_WRITE + nativeProtExec = C.PROT_EXEC +) + const ( keyRSS = "Rss" keyLastDetailLine = "VmFlags" @@ -99,7 +107,7 @@ func stateSegmentHead(in *bufio.Reader, out chan<- *MemorySegmentInfo, lastSeg * } func parseSegmentHead(line string) (*MemorySegmentInfo, error) { - line = strings.TrimSpace(line) + line = strings.TrimRight(line, "\n") matches := segmentHeadRegex.FindStringSubmatch(line) if len(matches) != expectedFieldCount { return nil, fmt.Errorf("invalid format \"%s\"", line) @@ -112,20 +120,17 @@ func parseSegmentHead(line string) (*MemorySegmentInfo, error) { addrStart, err := strconv.ParseUint(matches[fieldAddrStart], 16, 64) if err != nil { - return seg, fmt.Errorf("start address is not a valid hex number, %w", err) + return nil, fmt.Errorf("start address is not a valid hex number, %w", err) } seg.BaseAddress = uintptr(addrStart) seg.ParentBaseAddress = uintptr(addrStart) endStart, err := strconv.ParseUint(matches[fieldAddrEnd], 16, 64) if err != nil { - return seg, fmt.Errorf("end address is not a valid hex number, %w", err) + return nil, fmt.Errorf("end address is not a valid hex number, %w", err) } seg.Size = uintptr(endStart - addrStart) - if len(matches[fieldPerms]) != 4 { - return seg, fmt.Errorf("permissions have invalid length, expected exactly 4 characters") - } perms, err := ParsePermissions(matches[fieldPerms][0:3]) if err != nil { return seg, fmt.Errorf("permissions have invalid format, %w", err) @@ -137,7 +142,9 @@ func parseSegmentHead(line string) (*MemorySegmentInfo, error) { t = SegmentTypeMapped case 'p': t = SegmentTypePrivate - perms.COW = true + if perms.Write { + perms.COW = true + } default: return seg, errors.Newf("invalid memory type \"%c\"", matches[fieldPerms][3]) } @@ -147,9 +154,9 @@ func parseSegmentHead(line string) (*MemorySegmentInfo, error) { fpath := matches[fieldPathname] if fpath != "" && fpath[0] != '[' { - deleted := "(deleted)" - idxDeleted := strings.Index(fpath, deleted) - if idxDeleted == len(fpath)-len(deleted) /* ends with deleted */ { + deleted := " (deleted)" + idxDeleted := strings.LastIndex(fpath, deleted) + if idxDeleted >= 0 && idxDeleted == len(fpath)-len(deleted) /* ends with deleted */ { fpath = strings.TrimSpace(fpath[:idxDeleted]) } @@ -222,24 +229,44 @@ func parseBytes(value string) (uintptr, error) { return uintptr(amountUint) * detailUnitMultiplier, nil } +func sanitizeMappedFile(proc Process, seg *MemorySegmentInfo) { + if seg.MappedFile == nil { + return + } + originalPath := seg.MappedFile.Path() + if strings.Contains(originalPath, "\\012") { + // newline characters are encoded as '\012', but we cannot distinguish between an actual + // literal '\012' or it representing the newline character => lookup the link + linkPath := fmt.Sprintf("%s/%d/map_files/%x-%x", procPath, proc.PID(), seg.BaseAddress, seg.BaseAddress+seg.Size) + actualPath, err := os.Readlink(linkPath) + if err != nil { + // Could not read the link => leave it as-is + return + } + if originalPath != actualPath { + seg.MappedFile = fileio.NewFile(actualPath) + } + } +} + // PermissionsToNative converts the given Permissions to the // native linux representation. func PermissionsToNative(perms Permissions) int { - switch perms.String() { - case "R--": - return C.PROT_READ - case "RW-": - return C.PROT_READ | C.PROT_WRITE - case "RC-": - // Isn't actually COW, but RW is close enough - return C.PROT_READ | C.PROT_WRITE - case "--X": - return C.PROT_EXEC - case "RWX": - return C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC - case "RCX": - return C.PROT_READ | C.PROT_WRITE | C.PROT_EXEC - default: - return C.PROT_NONE + if !perms.Read && !perms.Write && !perms.Execute { + return nativeProtNone + } + + // COW does not translate to native permissions, so its ignored + + native := 0 + if perms.Read { + native |= nativeProtRead + } + if perms.Write { + native |= nativeProtWrite + } + if perms.Execute { + native |= nativeProtExec } + return native } diff --git a/procio/memory_linux_test.go b/procio/memory_linux_test.go new file mode 100644 index 0000000..a30ddbd --- /dev/null +++ b/procio/memory_linux_test.go @@ -0,0 +1,495 @@ +package procio + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "testing" + + "github.com/fkie-cad/yapscan/fileio" + + . "github.com/smartystreets/goconvey/convey" +) + +const ( + validSegmentSMEMEntry = `7f24d6cad000-7f24d6caf000 r--p 00034000 fe:03 921010 /usr/lib/ld-linux-x86-64.so.2 +Size: 8 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 8 kB +Pss: 8 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 8 kB +Referenced: 8 kB +Anonymous: 8 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +FilePmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 0 kB +THPeligible: 0 +VmFlags: rd mr mw me ac sd +` + invalidSegmentSMEMEntry = `7f24d6cad000-7f24d6caf000 BANANA 00034000 fe:03 921010 /usr/lib/ld-linux-x86-64.so.2 +Size: 8 kB +KernelPageSize: 4 kB +MMUPageSize: 4 kB +Rss: 8 kB +Pss: 8 kB +Shared_Clean: 0 kB +Shared_Dirty: 0 kB +Private_Clean: 0 kB +Private_Dirty: 8 kB +Referenced: 8 kB +Anonymous: 8 kB +LazyFree: 0 kB +AnonHugePages: 0 kB +ShmemPmdMapped: 0 kB +FilePmdMapped: 0 kB +Shared_Hugetlb: 0 kB +Private_Hugetlb: 0 kB +Swap: 0 kB +SwapPss: 0 kB +Locked: 0 kB +THPeligible: 0 +VmFlags: rd mr mw me ac sd +` +) + +func TestParseSMEMFile(t *testing.T) { + Convey("Valid SMEM data should be parsed correctly", t, func() { + rdr := bytes.NewBufferString(validSegmentSMEMEntry) + segments, err := parseSMEMFile(rdr) + So(err, ShouldBeNil) + So(segments, ShouldResemble, []*MemorySegmentInfo{ + { + ParentBaseAddress: 0x7f24d6cad000, + BaseAddress: 0x7f24d6cad000, + AllocatedPermissions: Permissions{Read: true}, + CurrentPermissions: Permissions{Read: true}, + Size: 0x2000, + RSS: 8 * 1024, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/usr/lib/ld-linux-x86-64.so.2", + }, + SubSegments: []*MemorySegmentInfo{}, + }, + }) + }) + Convey("An invalid SMEM entry followed by a valid entry should be parsed correctly but emit errors", t, func() { + rdr := bytes.NewBufferString(validSegmentSMEMEntry + invalidSegmentSMEMEntry) + segments, err := parseSMEMFile(rdr) + So(err, ShouldNotBeNil) + So(segments, ShouldResemble, []*MemorySegmentInfo{ + { + ParentBaseAddress: 0x7f24d6cad000, + BaseAddress: 0x7f24d6cad000, + AllocatedPermissions: Permissions{Read: true}, + CurrentPermissions: Permissions{Read: true}, + Size: 0x2000, + RSS: 8 * 1024, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/usr/lib/ld-linux-x86-64.so.2", + }, + SubSegments: []*MemorySegmentInfo{}, + }, + }) + }) +} + +func TestParseSegmentHead(t *testing.T) { + Convey("Invalid input format should error", t, func() { + info, err := parseSegmentHead("invalid") + So(info, ShouldBeNil) + So(err, ShouldNotBeNil) + }) + + Convey("Too large start address should error", t, func() { + info, err := parseSegmentHead("ffffffffffffffffff-0048a000 r-xp 00000000 fd:03 960637 /bin/bash") + So(info, ShouldBeNil) + So(err, ShouldNotBeNil) + }) + + Convey("Too large end address should error", t, func() { + info, err := parseSegmentHead("00400000-ffffffffffffffffff r-xp 00000000 fd:03 960637 /bin/bash") + So(info, ShouldBeNil) + So(err, ShouldNotBeNil) + }) + + Convey("Invalid permissions should error", t, func() { + _, err := parseSegmentHead("00400000-0048a000 pppp 00000000 fd:03 960637 /bin/bash") + So(err, ShouldNotBeNil) + }) + + Convey("Invalid mode should error", t, func() { + _, err := parseSegmentHead("00400000-0048a000 r-xx 00000000 fd:03 960637 /bin/bash") + So(err, ShouldNotBeNil) + }) + + Convey("A private file-backed segment should work", t, func() { + info, err := parseSegmentHead("00400000-0048a000 r-xp 00000000 fd:03 960637 /bin/bash") + So(err, ShouldBeNil) + So(info, ShouldResemble, &MemorySegmentInfo{ + ParentBaseAddress: 0x400000, + BaseAddress: 0x400000, + AllocatedPermissions: Permissions{ + Read: true, Execute: true, + }, + CurrentPermissions: Permissions{ + Read: true, Execute: true, + }, + Size: 0x8a000, + RSS: 0, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/bin/bash", + }, + SubSegments: []*MemorySegmentInfo{}, + }) + }) + + Convey("A private file-backed segment with whitespaces should work", t, func() { + info, err := parseSegmentHead("00400000-0048a000 r-xp 00000000 fd:03 960637 /bin/some path/with whitespaces.txt") + So(err, ShouldBeNil) + So(info, ShouldResemble, &MemorySegmentInfo{ + ParentBaseAddress: 0x400000, + BaseAddress: 0x400000, + AllocatedPermissions: Permissions{ + Read: true, Execute: true, + }, + CurrentPermissions: Permissions{ + Read: true, Execute: true, + }, + Size: 0x8a000, + RSS: 0, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/bin/some path/with whitespaces.txt", + }, + SubSegments: []*MemorySegmentInfo{}, + }) + }) + + Convey("A private file-backed segment with trailing whitespace should work", t, func() { + info, err := parseSegmentHead("00400000-0048a000 r-xp 00000000 fd:03 960637 /bin/bash ") + So(err, ShouldBeNil) + So(info, ShouldResemble, &MemorySegmentInfo{ + ParentBaseAddress: 0x400000, + BaseAddress: 0x400000, + AllocatedPermissions: Permissions{ + Read: true, Execute: true, + }, + CurrentPermissions: Permissions{ + Read: true, Execute: true, + }, + Size: 0x8a000, + RSS: 0, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/bin/bash ", + }, + SubSegments: []*MemorySegmentInfo{}, + }) + }) + + Convey("A shared file-backed segment should work", t, func() { + info, err := parseSegmentHead("00400000-0048a000 rwxs 00000000 fd:03 960637 /bin/bash") + So(err, ShouldBeNil) + So(info, ShouldResemble, &MemorySegmentInfo{ + ParentBaseAddress: 0x400000, + BaseAddress: 0x400000, + AllocatedPermissions: Permissions{ + Read: true, Write: true, Execute: true, + }, + CurrentPermissions: Permissions{ + Read: true, Write: true, Execute: true, + }, + Size: 0x8a000, + RSS: 0, + State: StateCommit, + Type: SegmentTypeMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/bin/bash", + }, + SubSegments: []*MemorySegmentInfo{}, + }) + }) + + Convey("A private file-backed segment with write should work", t, func() { + info, err := parseSegmentHead("00400000-0048a000 rwxp 00000000 fd:03 960637 /bin/bash") + So(err, ShouldBeNil) + So(info, ShouldResemble, &MemorySegmentInfo{ + ParentBaseAddress: 0x400000, + BaseAddress: 0x400000, + AllocatedPermissions: Permissions{ + Read: true, Write: true, COW: true, Execute: true, + }, + CurrentPermissions: Permissions{ + Read: true, Write: true, COW: true, Execute: true, + }, + Size: 0x8a000, + RSS: 0, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/bin/bash", + }, + SubSegments: []*MemorySegmentInfo{}, + }) + }) + + Convey("A private file-backed segment with a deleted file should work", t, func() { + info, err := parseSegmentHead("00400000-0048a000 rwxp 00000000 fd:03 960637 /bin/bash (deleted)") + So(err, ShouldBeNil) + So(info, ShouldResemble, &MemorySegmentInfo{ + ParentBaseAddress: 0x400000, + BaseAddress: 0x400000, + AllocatedPermissions: Permissions{ + Read: true, Write: true, COW: true, Execute: true, + }, + CurrentPermissions: Permissions{ + Read: true, Write: true, COW: true, Execute: true, + }, + Size: 0x8a000, + RSS: 0, + State: StateCommit, + Type: SegmentTypePrivateMapped, + MappedFile: &fileio.OSFile{ + FilePath: "/bin/bash", + }, + SubSegments: []*MemorySegmentInfo{}, + }) + }) +} + +func TestStateSegmentDetail(t *testing.T) { + Convey("Reading a key-value pair should work", t, func() { + rdr := bufio.NewReader(bytes.NewBufferString("SomeKey: Value\n")) + seg := new(MemorySegmentInfo) + + next, err := stateSegmentDetail(rdr, nil, seg) + So(next, ShouldEqual, stateSegmentDetail) + So(err, ShouldBeNil) + }) + + Convey("Reading an invalid key-value pair should error", t, func() { + rdr := bufio.NewReader(bytes.NewBufferString("Not a key value pair\n")) + seg := new(MemorySegmentInfo) + + next, err := stateSegmentDetail(rdr, nil, seg) + So(next, ShouldEqual, stateSegmentDetail) + So(err, ShouldNotBeNil) + }) + + Convey("Reading an invalid RSS-value should error", t, func() { + rdr := bufio.NewReader(bytes.NewBufferString("Rss: invalid\n")) + seg := new(MemorySegmentInfo) + + next, err := stateSegmentDetail(rdr, nil, seg) + So(next, ShouldEqual, stateSegmentDetail) + So(err, ShouldNotBeNil) + }) + + Convey("Reading an RSS-value should set it", t, func() { + rdr := bufio.NewReader(bytes.NewBufferString("Rss: 4 kB\n")) + seg := new(MemorySegmentInfo) + + next, err := stateSegmentDetail(rdr, nil, seg) + So(next, ShouldEqual, stateSegmentDetail) + So(err, ShouldBeNil) + So(seg.RSS, ShouldEqual, 4*1024) + }) + + Convey("Reading a 0-RSS-value should set it and StateReserve", t, func() { + rdr := bufio.NewReader(bytes.NewBufferString("Rss: 0 kB\n")) + seg := new(MemorySegmentInfo) + + next, err := stateSegmentDetail(rdr, nil, seg) + So(next, ShouldEqual, stateSegmentDetail) + So(err, ShouldBeNil) + So(seg.RSS, ShouldEqual, 0) + So(seg.State, ShouldEqual, StateReserve) + }) + + Convey("Encountering an error during reading should emit that error", t, func() { + rdr := bufio.NewReader(bytes.NewBufferString("the missing newline will cause an EOF")) + seg := new(MemorySegmentInfo) + + next, err := stateSegmentDetail(rdr, nil, seg) + So(next, ShouldEqual, stateSegmentDetail) + So(err, ShouldBeError, io.EOF) + }) +} + +func TestParseKeyValue(t *testing.T) { + Convey("Parsing a valid key-value pair should work", t, func() { + key, value, err := parseKeyValue("somekey: someval") + So(err, ShouldBeNil) + So(key, ShouldEqual, "somekey") + So(value, ShouldEqual, "someval") + }) + + Convey("Parsing invalid input should error", t, func() { + _, _, err := parseKeyValue("notAKeyValue") + So(err, ShouldNotBeNil) + }) +} + +func TestParseBytes(t *testing.T) { + Convey("Parsing input with multiple spaces should error", t, func() { + _, err := parseBytes("something thats not bytes") + So(err, ShouldNotBeNil) + }) + + Convey("Parsing input with an unsupported unit should error", t, func() { + // Only "kB" is supported + _, err := parseBytes("42 GB") + So(err, ShouldNotBeNil) + }) + + Convey("Parsing input with something that's not a number should error", t, func() { + // Only "kB" is supported + _, err := parseBytes("allthe kB") + So(err, ShouldNotBeNil) + }) + + Convey("Parsing valid input should work", t, func() { + // Only "kB" is supported + bytes, err := parseBytes("42 kB") + So(bytes, ShouldEqual, 42*1024) + So(err, ShouldBeNil) + }) +} + +func TestPermissionsToNative(t *testing.T) { + parameters := []struct { + perm Permissions + native int + }{ + {Permissions{}, nativeProtNone}, + {Permissions{Read: true}, nativeProtRead}, + {Permissions{Write: true}, nativeProtWrite}, + {Permissions{Execute: true}, nativeProtExec}, + {Permissions{Read: true, Write: true}, nativeProtRead | nativeProtWrite}, + {Permissions{Read: true, Execute: true}, nativeProtRead | nativeProtExec}, + {Permissions{Write: true, Execute: true}, nativeProtWrite | nativeProtExec}, + {Permissions{Read: true, Write: true, Execute: true}, nativeProtRead | nativeProtWrite | nativeProtExec}, + } + + for _, param := range parameters { + Convey(fmt.Sprintf("%s should decode correctly", param.perm), t, func() { + native := PermissionsToNative(param.perm) + So(native, ShouldEqual, param.native) + }) + } +} + +func TestSanitizeMappedFile(t *testing.T) { + Convey("Sanitizing a segment without a mapped file should do nothing", t, func() { + proc := NewMockProcess(t) + + seg := &MemorySegmentInfo{} + sanitizeMappedFile(proc, seg) + So(seg, ShouldResemble, &MemorySegmentInfo{}) + }) + + Convey("Sanitizing a segment with a mapped file", t, func() { + Convey("without any special escapes should do nothing", func() { + proc := NewMockProcess(t) + + seg := &MemorySegmentInfo{ + MappedFile: fileio.NewFile("/some/normal/path"), + } + + sanitizeMappedFile(proc, seg) + + So(seg, ShouldResemble, &MemorySegmentInfo{ + MappedFile: fileio.NewFile("/some/normal/path"), + }) + }) + + Convey("with a newline escape sequence", func() { + pid := 42 + proc := NewMockProcess(t) + proc.On("PID").Return(pid) + + Convey("where the link is non-existent should do nothing", func() { + seg := &MemorySegmentInfo{ + MappedFile: fileio.NewFile("/path/withEscapeSequence\\012"), + } + + sanitizeMappedFile(proc, seg) + + So(seg, ShouldResemble, &MemorySegmentInfo{ + MappedFile: fileio.NewFile("/path/withEscapeSequence\\012"), + }) + }) + + Convey("but the link shows its a literal '\\012' should do nothing", func() { + origProcPath := procPath + defer func() { + procPath = origProcPath + }() + + tempdir := t.TempDir() + procPath = tempdir + + mappedName := "/path/withEscapeSequence\\012/but/notANewline" + seg := &MemorySegmentInfo{ + MappedFile: fileio.NewFile(mappedName), + } + + mapFilesPath := fmt.Sprintf("%s/%d/map_files", tempdir, pid) + os.MkdirAll(mapFilesPath, 0700) + os.Symlink(mappedName, fmt.Sprintf("%s/%x-%x", mapFilesPath, seg.BaseAddress, seg.BaseAddress+seg.Size)) + + sanitizeMappedFile(proc, seg) + + So(seg, ShouldResemble, &MemorySegmentInfo{ + MappedFile: fileio.NewFile(mappedName), + }) + }) + + Convey("and its a newline character should replace the path", func() { + origProcPath := procPath + defer func() { + procPath = origProcPath + }() + + tempdir := t.TempDir() + procPath = tempdir + + mappedName := "/path/withEscapeSequence\\012/asNewline" + readName := "/path/withEscapeSequence\n/asNewline" + seg := &MemorySegmentInfo{ + MappedFile: fileio.NewFile(mappedName), + } + + mapFilesPath := fmt.Sprintf("%s/%d/map_files", tempdir, pid) + os.MkdirAll(mapFilesPath, 0700) + os.Symlink(readName, fmt.Sprintf("%s/%x-%x", mapFilesPath, seg.BaseAddress, seg.BaseAddress+seg.Size)) + + sanitizeMappedFile(proc, seg) + + So(seg, ShouldResemble, &MemorySegmentInfo{ + MappedFile: fileio.NewFile(readName), + }) + }) + }) + }) +} diff --git a/procio/mock_CachingProcess_test.go b/procio/mock_CachingProcess_test.go new file mode 100644 index 0000000..58366a3 --- /dev/null +++ b/procio/mock_CachingProcess_test.go @@ -0,0 +1,175 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package procio + +import ( + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// MockCachingProcess is an autogenerated mock type for the CachingProcess type +type MockCachingProcess struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *MockCachingProcess) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Crash provides a mock function with given fields: _a0 +func (_m *MockCachingProcess) Crash(_a0 CrashMethod) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(CrashMethod) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Handle provides a mock function with given fields: +func (_m *MockCachingProcess) Handle() interface{} { + ret := _m.Called() + + var r0 interface{} + if rf, ok := ret.Get(0).(func() interface{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + return r0 +} + +// Info provides a mock function with given fields: +func (_m *MockCachingProcess) Info() (*ProcessInfo, error) { + ret := _m.Called() + + var r0 *ProcessInfo + if rf, ok := ret.Get(0).(func() *ProcessInfo); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ProcessInfo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// InvalidateCache provides a mock function with given fields: +func (_m *MockCachingProcess) InvalidateCache() { + _m.Called() +} + +// MemorySegments provides a mock function with given fields: +func (_m *MockCachingProcess) MemorySegments() ([]*MemorySegmentInfo, error) { + ret := _m.Called() + + var r0 []*MemorySegmentInfo + if rf, ok := ret.Get(0).(func() []*MemorySegmentInfo); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*MemorySegmentInfo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PID provides a mock function with given fields: +func (_m *MockCachingProcess) PID() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// Resume provides a mock function with given fields: +func (_m *MockCachingProcess) Resume() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// String provides a mock function with given fields: +func (_m *MockCachingProcess) String() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Suspend provides a mock function with given fields: +func (_m *MockCachingProcess) Suspend() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewMockCachingProcess creates a new instance of MockCachingProcess. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockCachingProcess(t testing.TB) *MockCachingProcess { + mock := &MockCachingProcess{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/procio/mock_MemoryReaderFactory_test.go b/procio/mock_MemoryReaderFactory_test.go new file mode 100644 index 0000000..67a7c2b --- /dev/null +++ b/procio/mock_MemoryReaderFactory_test.go @@ -0,0 +1,47 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package procio + +import ( + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// MockMemoryReaderFactory is an autogenerated mock type for the MemoryReaderFactory type +type MockMemoryReaderFactory struct { + mock.Mock +} + +// NewMemoryReader provides a mock function with given fields: proc, seg +func (_m *MockMemoryReaderFactory) NewMemoryReader(proc Process, seg *MemorySegmentInfo) (MemoryReader, error) { + ret := _m.Called(proc, seg) + + var r0 MemoryReader + if rf, ok := ret.Get(0).(func(Process, *MemorySegmentInfo) MemoryReader); ok { + r0 = rf(proc, seg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(MemoryReader) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(Process, *MemorySegmentInfo) error); ok { + r1 = rf(proc, seg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewMockMemoryReaderFactory creates a new instance of MockMemoryReaderFactory. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockMemoryReaderFactory(t testing.TB) *MockMemoryReaderFactory { + mock := &MockMemoryReaderFactory{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/procio/mock_MemoryReader_test.go b/procio/mock_MemoryReader_test.go new file mode 100644 index 0000000..1044a95 --- /dev/null +++ b/procio/mock_MemoryReader_test.go @@ -0,0 +1,80 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package procio + +import ( + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// MockMemoryReader is an autogenerated mock type for the MemoryReader type +type MockMemoryReader struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *MockMemoryReader) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Read provides a mock function with given fields: p +func (_m *MockMemoryReader) Read(p []byte) (int, error) { + ret := _m.Called(p) + + var r0 int + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Seek provides a mock function with given fields: offset, whence +func (_m *MockMemoryReader) Seek(offset int64, whence int) (int64, error) { + ret := _m.Called(offset, whence) + + var r0 int64 + if rf, ok := ret.Get(0).(func(int64, int) int64); ok { + r0 = rf(offset, whence) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(int64, int) error); ok { + r1 = rf(offset, whence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewMockMemoryReader creates a new instance of MockMemoryReader. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockMemoryReader(t testing.TB) *MockMemoryReader { + mock := &MockMemoryReader{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/procio/mock_Process_test.go b/procio/mock_Process_test.go new file mode 100644 index 0000000..d9e0f3b --- /dev/null +++ b/procio/mock_Process_test.go @@ -0,0 +1,170 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package procio + +import ( + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// MockProcess is an autogenerated mock type for the Process type +type MockProcess struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *MockProcess) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Crash provides a mock function with given fields: _a0 +func (_m *MockProcess) Crash(_a0 CrashMethod) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(CrashMethod) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Handle provides a mock function with given fields: +func (_m *MockProcess) Handle() interface{} { + ret := _m.Called() + + var r0 interface{} + if rf, ok := ret.Get(0).(func() interface{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + return r0 +} + +// Info provides a mock function with given fields: +func (_m *MockProcess) Info() (*ProcessInfo, error) { + ret := _m.Called() + + var r0 *ProcessInfo + if rf, ok := ret.Get(0).(func() *ProcessInfo); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*ProcessInfo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MemorySegments provides a mock function with given fields: +func (_m *MockProcess) MemorySegments() ([]*MemorySegmentInfo, error) { + ret := _m.Called() + + var r0 []*MemorySegmentInfo + if rf, ok := ret.Get(0).(func() []*MemorySegmentInfo); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*MemorySegmentInfo) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PID provides a mock function with given fields: +func (_m *MockProcess) PID() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// Resume provides a mock function with given fields: +func (_m *MockProcess) Resume() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// String provides a mock function with given fields: +func (_m *MockProcess) String() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// Suspend provides a mock function with given fields: +func (_m *MockProcess) Suspend() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewMockProcess creates a new instance of MockProcess. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockProcess(t testing.TB) *MockProcess { + mock := &MockProcess{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/procio/mock_memoryReaderImpl_test.go b/procio/mock_memoryReaderImpl_test.go new file mode 100644 index 0000000..6c34c46 --- /dev/null +++ b/procio/mock_memoryReaderImpl_test.go @@ -0,0 +1,112 @@ +// Code generated by mockery v2.12.2. DO NOT EDIT. + +package procio + +import ( + testing "testing" + + mock "github.com/stretchr/testify/mock" +) + +// mockMemoryReaderImpl is an autogenerated mock type for the memoryReaderImpl type +type mockMemoryReaderImpl struct { + mock.Mock +} + +// Close provides a mock function with given fields: +func (_m *mockMemoryReaderImpl) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Process provides a mock function with given fields: +func (_m *mockMemoryReaderImpl) Process() Process { + ret := _m.Called() + + var r0 Process + if rf, ok := ret.Get(0).(func() Process); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(Process) + } + } + + return r0 +} + +// Read provides a mock function with given fields: p +func (_m *mockMemoryReaderImpl) Read(p []byte) (int, error) { + ret := _m.Called(p) + + var r0 int + if rf, ok := ret.Get(0).(func([]byte) int); ok { + r0 = rf(p) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Seek provides a mock function with given fields: offset, whence +func (_m *mockMemoryReaderImpl) Seek(offset int64, whence int) (int64, error) { + ret := _m.Called(offset, whence) + + var r0 int64 + if rf, ok := ret.Get(0).(func(int64, int) int64); ok { + r0 = rf(offset, whence) + } else { + r0 = ret.Get(0).(int64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(int64, int) error); ok { + r1 = rf(offset, whence) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Segment provides a mock function with given fields: +func (_m *mockMemoryReaderImpl) Segment() *MemorySegmentInfo { + ret := _m.Called() + + var r0 *MemorySegmentInfo + if rf, ok := ret.Get(0).(func() *MemorySegmentInfo); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*MemorySegmentInfo) + } + } + + return r0 +} + +// newMockMemoryReaderImpl creates a new instance of mockMemoryReaderImpl. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. +func newMockMemoryReaderImpl(t testing.TB) *mockMemoryReaderImpl { + mock := &mockMemoryReaderImpl{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/procio/proc_linux.go b/procio/proc_linux.go new file mode 100644 index 0000000..9b2afad --- /dev/null +++ b/procio/proc_linux.go @@ -0,0 +1,7 @@ +package procio + +// procPath is the path to the /proc pseudo-filesystem +// this should not be touched in production code, but +// can be used during testing in order to provide a +// mock /proc fs. +var procPath = "/proc" diff --git a/procio/process_linux.go b/procio/process_linux.go index 680ca28..f1ea483 100644 --- a/procio/process_linux.go +++ b/procio/process_linux.go @@ -23,7 +23,7 @@ type processLinux struct { } func tryReadingSmaps(pid int) error { - smaps, err := os.OpenFile(fmt.Sprintf("/proc/%d/smaps", pid), os.O_RDONLY, 0444) + smaps, err := os.OpenFile(fmt.Sprintf("%s/%d/smaps", procPath, pid), os.O_RDONLY, 0444) if err != nil { return err } @@ -34,7 +34,7 @@ func tryReadingSmaps(pid int) error { // GetRunningPIDs returns the PIDs of all running processes. func GetRunningPIDs() ([]int, error) { - maps, _ := filepath.Glob("/proc/*/smaps") + maps, _ := filepath.Glob(fmt.Sprintf("%s/*/smaps", procPath)) pids := make([]int, 0, len(maps)-2) for _, path := range maps { @@ -60,7 +60,7 @@ func GetRunningPIDs() ([]int, error) { } func open(pid int) (Process, error) { - _, err := os.Stat(fmt.Sprintf("/proc/%d", pid)) + _, err := os.Stat(fmt.Sprintf("%s/%d", procPath, pid)) if os.IsNotExist(err) { return nil, fmt.Errorf("process does not exist") } @@ -84,7 +84,7 @@ func (p *processLinux) Info() (*ProcessInfo, error) { PID: p.pid, } - procInfo, tmpErr := os.Stat(fmt.Sprintf("/proc/%d", p.pid)) + procInfo, tmpErr := os.Stat(fmt.Sprintf("%s/%d", procPath, p.pid)) if tmpErr != nil { err = errors.NewMultiError(err, fmt.Errorf("could not determine process owner, reason: %w", tmpErr)) } else if stat, ok := procInfo.Sys().(*syscall.Stat_t); ok { @@ -95,7 +95,7 @@ func (p *processLinux) Info() (*ProcessInfo, error) { info.Username = u.Username } - procExeLink := fmt.Sprintf("/proc/%d/exe", p.pid) + procExeLink := fmt.Sprintf("%s/%d/exe", procPath, p.pid) info.ExecutablePath, tmpErr = os.Readlink(procExeLink) if tmpErr != nil { err = errors.NewMultiError(err, fmt.Errorf("could not determine executable path, reason: %w", tmpErr)) @@ -180,13 +180,17 @@ func (p *processLinux) Close() error { } func (p *processLinux) MemorySegments() ([]*MemorySegmentInfo, error) { - smaps, err := os.OpenFile(fmt.Sprintf("/proc/%d/smaps", p.pid), os.O_RDONLY, 0444) + smaps, err := os.OpenFile(fmt.Sprintf("%s/%d/smaps", procPath, p.pid), os.O_RDONLY, 0444) if err != nil { return nil, err } defer smaps.Close() - return parseSMEMFile(smaps) + segments, err := parseSMEMFile(smaps) + for _, seg := range segments { + sanitizeMappedFile(p, seg) + } + return segments, err } func (p *processLinux) Crash(m CrashMethod) error { diff --git a/procio/procio.go b/procio/procio.go new file mode 100644 index 0000000..b2f69fe --- /dev/null +++ b/procio/procio.go @@ -0,0 +1,2 @@ +//go:generate mockery --inpackage --testonly --name ".*" +package procio diff --git a/procio/reader_linux.go b/procio/reader_linux.go index 0736828..82afb49 100644 --- a/procio/reader_linux.go +++ b/procio/reader_linux.go @@ -21,7 +21,7 @@ type memfileReader struct { } func newMemoryReader(proc Process, seg *MemorySegmentInfo) (memoryReaderImpl, error) { - memfile, err := os.OpenFile(fmt.Sprintf("/proc/%d/mem", proc.PID()), os.O_RDONLY, 0400) + memfile, err := os.OpenFile(fmt.Sprintf("%s/%d/mem", procPath, proc.PID()), os.O_RDONLY, 0400) if err != nil { return nil, fmt.Errorf("could not open process memory for reading, reason: %w", err) } diff --git a/testutil/memory/main.go b/testutil/memory/main.go index 02bf49a..f67aca2 100644 --- a/testutil/memory/main.go +++ b/testutil/memory/main.go @@ -24,7 +24,8 @@ func Main() { ensureStdinBinary() - size, err := strconv.ParseUint(os.Args[1], 10, 64) + // 31 bits because we need to convert it to int later + size, err := strconv.ParseUint(os.Args[1], 10, 31) if err != nil { fmt.Printf(OutputErrorPrefix+"Invalid size value, %v\n", err) os.Exit(1) diff --git a/version/version.go b/version/version.go index fe826d8..3a8c441 100644 --- a/version/version.go +++ b/version/version.go @@ -9,7 +9,7 @@ import ( var YapscanVersion = Version{ Major: 0, - Minor: 15, + Minor: 16, Bugfix: 0, }