From 9fbedb848111dc3f96513aefcbf37e5117440898 Mon Sep 17 00:00:00 2001 From: Jef LeCompte Date: Fri, 30 Sep 2022 11:05:03 -0700 Subject: [PATCH] feat: become a gh extension --- .github/dependabot.yaml | 6 - .github/workflows/ci.yaml | 29 ++-- .github/workflows/release.yaml | 63 ++------- .gitignore | 3 +- Makefile | 41 ------ README.md | 67 +++++++--- go.mod | 4 +- go.sum | 123 +++++++++++++---- cmd/audit_org_keys/main.go => main.go | 183 ++++++++++++++------------ 9 files changed, 273 insertions(+), 246 deletions(-) delete mode 100644 Makefile rename cmd/audit_org_keys/main.go => main.go (73%) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 6dd83f6..918b880 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,11 +1,5 @@ version: 2 updates: - - package-ecosystem: "docker" - directory: "/" - schedule: - interval: "daily" - labels: - - "dependencies" - package-ecosystem: "github-actions" directory: "/" schedule: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e1a1f14..194600c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,37 +9,24 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: - go-version: '1.16' - - name: Setup build cache - uses: actions/cache@v2 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go- + go-version: "1.19" - name: Build service - run: make build + run: go build lint: name: Lint runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: '1.16' - - name: Setup build cache - uses: actions/cache@v2 + uses: actions/setup-go@v3 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go- + go-version: "1.19" - name: Lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 with: args: --enable dupl,gofmt,revive - skip-go-installation: true diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 963c488..f0eef43 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,66 +4,31 @@ on: branches: - main jobs: - release-please: - name: Run Release Please + release: + name: Create release runs-on: ubuntu-latest outputs: - release-created: ${{ steps.release.outputs.release_created }} - upload-url: ${{ steps.release.outputs.upload_url }} + release_created: ${{ steps.release.outputs.release_created }} steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup release please - uses: google-github-actions/release-please-action@v2 id: release + uses: google-github-actions/release-please-action@v3 with: token: ${{ secrets.GITHUB_TOKEN }} release-type: simple - package-name: audit-org-keys - build-publish: - name: Build and publish assets + package-name: gh-vanity + upload_assets: + name: Upload assets runs-on: ubuntu-latest - needs: release-please - if: needs.release-please.outputs.release-created + needs: release + if: needs.release.outputs.release_created == 'true' steps: - name: Checkout repository - uses: actions/checkout@v2 - - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/checkout@v3 with: - go-version: '1.16' - - name: Setup build cache - uses: actions/cache@v2 + fetch-depth: 0 + - uses: cli/gh-extension-precompile@v1 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go- - - name: Build release assets - run: make dist - - name: Upload Windows asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.release-please.outputs.upload-url }} - asset_path: ./audit-org-keys-windows-amd64.exe - asset_name: audit-org-keys-windows-amd64.exe - asset_content_type: application/octet-stream - - name: Upload Linux asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.release-please.outputs.upload-url }} - asset_path: ./audit-org-keys-linux-amd64 - asset_name: audit-org-keys-linux-amd64 - asset_content_type: application/octet-stream - - name: Upload macOS asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ needs.release-please.outputs.upload-url }} - asset_path: ./audit-org-keys-darwin-amd64 - asset_name: audit-org-keys-darwin-amd64 - asset_content_type: application/octet-stream + go_version: "1.19" diff --git a/.gitignore b/.gitignore index c875fbd..1406d6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ -bin/ + +audit-org-keys diff --git a/Makefile b/Makefile deleted file mode 100644 index 4dd9048..0000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -PROJECT_NAME=audit-org-keys - -GOCMD=$(shell pwd)/cmd/$(subst -,_,$(PROJECT_NAME)) -GOBIN=$(shell pwd)/bin/$(subst -,_,$(PROJECT_NAME)) -GOREPORTS=$(shell pwd)/bin -GO_MAJOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) -GO_MINOR_VERSION = $(shell go version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) -MINIMUM_SUPPORTED_GO_MAJOR_VERSION = 1 -MINIMUM_SUPPORTED_GO_MINOR_VERSION = 16 -GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION).$(MINIMUM_SUPPORTED_GO_MINOR_VERSION) -.SILENT: - -.DEFAULT: -build: validate-go-version - go build -o $(GOBIN) $(GOCMD) - -dist: validate-go-version - GOOS=darwin GOARCH=amd64 go build -o $(PROJECT_NAME)-darwin-amd64 $(GOCMD) - GOOS=linux GOARCH=amd64 go build -o $(PROJECT_NAME)-linux-amd64 $(GOCMD) - GOOS=windows GOARCH=amd64 go build -o $(PROJECT_NAME)-windows-amd64.exe $(GOCMD) - -fmt: validate-go-version - gofmt -s -w . - -lint: validate-go-version - golangci-lint run --enable dupl,gofmt,revive - -test: validate-go-version - mkdir -p $(GOREPORTS) - go test -v ./... -coverprofile=$(GOREPORTS)/coverage.out -json > $(GOREPORTS)/report.json - -validate-go-version: - if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ - exit 0 ;\ - elif [ $(GO_MAJOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \ - echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\ - exit 1; \ - elif [ $(GO_MINOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MINOR_VERSION) ] ; then \ - echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\ - exit 1; \ - fi diff --git a/README.md b/README.md index 8ff411e..bedad68 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# audit-org-keys [![Release](https://img.shields.io/github/workflow/status/jef/audit-org-keys/Release?color=24292e&label=Release&logo=github&logoColor=white&style=flat-square)](https://github.com/jef/audit-org-keys/actions/workflows/release.yaml) +# gh-audit-org-keys [![Release](https://github.com/jef/gh-audit-org-keys/actions/workflows/release.yaml/badge.svg)](https://github.com/jef/gh-vanity/actions/workflows/release.yaml) The point of this project is to help demonstrate that users of GitHub could potentially fall victim to getting their private SSH key cracked. This based on the size and complexity of the key the user generates. @@ -6,31 +6,68 @@ Programs like `ssh2john` from **John the Ripper** can best demonstrate how fast ## Installation -`go get -u github.com/jef/audit-org-keys/cmd/audit_org_keys` +1. Install the `gh` cli - see the [installation](https://github.com/cli/cli#installation) -Also available under [GitHub Releases](https://github.com/jef/audit-org-keys/releases) as an executable. + _Installation requires a minimum version (2.0.0) of the GitHub CLI that supports extensions._ + +2. Install this extension: + + ```shell + gh extension install jef/gh-audit-org-keys + ``` + +
+Manual Installation + +Requirements: `cli/cli` and `go`. + +1. Clone the repository + + ```shell + # git + git clone git@github.com:jef/gh-audit-org-keys.git + + # GitHub CLI + gh repo clone jef/gh-audit-org-keys + ``` + +2. `cd` into it + + ```shell + cd gh-audit-org-keys + ``` + +3. Build it + + ```shell + make build + ``` + +4. Install it locally + + ```shell + gh extension install . + ``` +
## Usage -It is required that you use a GitHub Personal Access Token (PAT). You can generate one [here](https://github.com/settings/tokens/new). The required scopes are `['read:org']`. Set your PAT to environment variable `GITHUB_TOKEN`. If `GITHUB_TOKEN` isn't set, then you may not get the results you expect. +To run: ```shell -Usage of audit_org_keys: - -o, --organization string [required] GitHub organization provided to inspect - -s, --show-users all display users with filter (all, `with`, `without`, `multiple`) +gh audit-org-keys ``` -### Examples +To upgrade: -- `audit-org-keys --organization="actions"` -- `audit-org-keys --organization="actions" --show-users="all"` +```sh +gh extension upgrade audit-org-keys +``` -## Releases +### Examples -| Tag | Description | -|:---:|---| -| `latest` | Built against tagged releases; stable -| `nightly` | Built against HEAD; generally considered stable, but could have problems | +- `gh audit-org-keys --organization="actions"` +- `gh audit-org-keys --organization="actions" --show-users="all"` ### Acknowledgments diff --git a/go.mod b/go.mod index 45fe53b..a7feed3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/jef/audit-org-keys go 1.16 require ( + github.com/cli/go-gh v0.1.1 github.com/olekukonko/tablewriter v0.0.5 - github.com/rs/zerolog v1.23.0 - github.com/spf13/pflag v1.0.5 + github.com/spf13/cobra v1.5.0 ) diff --git a/go.sum b/go.sum index e860516..e33a09a 100644 --- a/go.sum +++ b/go.sum @@ -1,34 +1,103 @@ -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da/go.mod h1:HXz79SMFnF9arKxqeoHWxmo1BhplAH7wehlRhKQIL94= +github.com/cli/browser v1.1.0/go.mod h1:HKMQAt9t12kov91Mn7RfZxyJQQgWgyS/3SZswlZ5iTI= +github.com/cli/go-gh v0.1.1 h1:hiCC3yeyTQrWhwUkbUb+ukONB8/2HgcjfKQjmYFnnCA= +github.com/cli/go-gh v0.1.1/go.mod h1:bqxLdCoTZ73BuiPEJx4olcO/XKhVZaFDchFagYRBweE= +github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= +github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= +github.com/cli/shurcooL-graphql v0.0.2 h1:rwP5/qQQ2fM0TzkUTwtt6E2LbIYf6R+39cUXTa04NYk= +github.com/cli/shurcooL-graphql v0.0.2/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= +github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= +github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/itchyny/gojq v0.12.8/go.mod h1:gE2kZ9fVRU0+JAksaTzjIlgnCa2akU+a1V0WXgJQN5c= +github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= +github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.11.0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc= +github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g= -github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo= +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/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= +github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -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/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= +github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50 h1:vKyz8L3zkd+xrMeIaBsQ/MNVPVFSffdaU3ZyYlBGFnI= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -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-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/cmd/audit_org_keys/main.go b/main.go similarity index 73% rename from cmd/audit_org_keys/main.go rename to main.go index a76e5c4..b0a0265 100644 --- a/cmd/audit_org_keys/main.go +++ b/main.go @@ -1,40 +1,32 @@ package main import ( - "encoding/json" + _ "embed" "fmt" - "io/ioutil" - "net/http" + "log" "os" "regexp" "strings" "sync" "sync/atomic" + "github.com/cli/go-gh" "github.com/olekukonko/tablewriter" - "github.com/rs/zerolog/log" - "github.com/spf13/pflag" + "github.com/spf13/cobra" ) -const ( - gitHubURL = "https://github.com" - gitHubOrgAPI = "https://api.github.com/orgs" -) +//go:embed version.txt +var Version string -var ( - gitHubToken = os.Getenv("GITHUB_TOKEN") -) +func init() { + cobra.OnInitialize() +} type member struct { Login string `json:"login"` Keys []string } -type cliOptions struct { - ShowUsers string - GitHubOrg string -} - type keyTable struct { keyDsaSize uint32 keyEcdsaSize uint32 @@ -77,29 +69,34 @@ func printReport(kt keyTable) { fmt.Sprintf("%d (%.2f%%)", kt.keyRsaSize, float32(kt.keyRsaSize)/float32(kt.keySize)*100), fmt.Sprintf("%d (%.2f%%)", kt.userRsaSize, float32(kt.userRsaSize)/float32(kt.userSize)*100)}, } + withoutKey := [][]string{ {"users without keys", "", "", fmt.Sprintf("%d (%.2f%%)", kt.userWithoutKeySize, float32(kt.userWithoutKeySize)/float32(kt.userSize)*100)}, } + withMultipleKey := [][]string{{"users with multiple keys", "", "", fmt.Sprintf("%d (%.2f%%)", kt.userWithMultipleKeySize, float32(kt.userWithMultipleKeySize)/float32(kt.userSize)*100)}, } + strongKey := [][]string{ {"users with strong keys", "", fmt.Sprintf("%d (%.2f%%)", kt.strongKeySize, float32(kt.strongKeySize)/float32(kt.keySize)*100), fmt.Sprintf("%d (%.2f%%)", kt.userWithStrongKeySize, float32(kt.userWithStrongKeySize)/float32(kt.userWithKeySize)*100)}, } + weakKey := [][]string{ {"users with weak keys", "", fmt.Sprintf("%d (%.2f%%)", kt.weakKeySize, float32(kt.weakKeySize)/float32(kt.keySize)*100), fmt.Sprintf("%d (%.2f%%)", kt.userWithWeakKeySize, float32(kt.userWithWeakKeySize)/float32(kt.userWithKeySize)*100)}, } + t := tablewriter.NewWriter(os.Stdout) t.SetHeader([]string{"description", "key type", "# of keys", "# of users"}) t.SetHeaderColor(tablewriter.Colors{tablewriter.FgCyanColor}, @@ -118,23 +115,27 @@ func printReport(kt keyTable) { t.AppendBulk(strongKey) t.AppendBulk(weakKey) t.Render() + if len(kt.userWithWeakKey) > 0 { - log.Info().Msg("users with weak keys:") + log.Println("users with weak keys:") for _, m := range kt.userWithWeakKey { - log.Info().Msgf("%s", m.Login) + log.Printf("%s\n", m.Login) } } } -func (o cliOptions) generateKeyTable(ms []member) keyTable { +func generateKeyTable(showUsers string, ms []member) keyTable { var kt keyTable kt.userSize = uint32(len(ms)) var wg sync.WaitGroup + for _, m := range ms { wg.Add(1) m := m + go func() { defer wg.Done() + var ( hasDsa bool hasEcdsa bool @@ -142,6 +143,7 @@ func (o cliOptions) generateKeyTable(ms []member) keyTable { hasRsa bool userHasStrongRsa bool ) + for _, key := range m.Keys { atomic.AddUint32(&kt.keySize, 1) switch { @@ -171,6 +173,7 @@ func (o cliOptions) generateKeyTable(ms []member) keyTable { } } } + switch { case hasDsa: atomic.AddUint32(&kt.userDsaSize, 1) @@ -192,110 +195,94 @@ func (o cliOptions) generateKeyTable(ms []member) keyTable { kt.userWithWeakKey = append(kt.userWithWeakKey, m) } } + if len(m.Keys) == 0 { atomic.AddUint32(&kt.userWithoutKeySize, 1) - if o.ShowUsers == "without" || o.ShowUsers == "all" { - log.Info(). - Str("user", m.Login). - Strs("keys", m.Keys). - Msg("retrieved keys") + if showUsers == "without" || showUsers == "all" { + log.Printf("retrieved keys for [%s]: %s", m.Login, m.Keys) } } + if len(m.Keys) > 0 { atomic.AddUint32(&kt.userWithKeySize, 1) - if o.ShowUsers == "with" || o.ShowUsers == "all" { - log.Info(). - Str("user", m.Login). - Strs("keys", m.Keys). - Msg("retrieved keys") + if showUsers == "with" || showUsers == "all" { + log.Printf("retrieved keys for [%s]: %s", m.Login, m.Keys) } } + if len(m.Keys) > 1 { atomic.AddUint32(&kt.userWithMultipleKeySize, 1) - if o.ShowUsers == "multiple" || o.ShowUsers == "all" { - log.Info(). - Str("user", m.Login). - Strs("keys", m.Keys). - Msg("retrieved keys") + if showUsers == "multiple" || showUsers == "all" { + log.Printf("retrieved keys for [%s]: %s", m.Login, m.Keys) } } }() } + wg.Wait() + return kt } func isRsaStrong(key string) bool { r := regexp.MustCompile(`(ssh-rsa) (.*)`) keyArray := r.FindStringSubmatch(key) + return len(keyArray[2]) >= 372 } func getKeys(ms []member) []member { - client := &http.Client{} + client, err := gh.RESTClient(nil) + if err != nil { + log.Fatalf("failed to create REST client: %v", err) + } + var wg sync.WaitGroup + for i := 0; i < len(ms); i++ { wg.Add(1) go func(i int) { m := &ms[i] defer wg.Done() - req, err := http.NewRequest( - "GET", - fmt.Sprintf("%s/%s.keys", gitHubURL, m.Login), - nil, - ) - if err != nil { - log.Fatal().Err(err) + var KeysT []struct { + Id int `json:"id"` + Key string `json:"key"` } - res, err := client.Do(req) + err = client.Get(fmt.Sprintf("users/%s/keys", m.Login), &KeysT) if err != nil { - log.Fatal().Err(err) + log.Fatalf("failed to get keys for [%s]: %v", m.Login, err) } - defer res.Body.Close() - - key, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Fatal().Err(err) - } else if (len(key)) != 0 { - m.Keys = strings.Split(strings.TrimSpace(string(key)), "\n") + for _, key := range KeysT { + m.Keys = append(m.Keys, key.Key) } }(i) } + wg.Wait() + return ms } -func (o cliOptions) getMembers() []member { +func getMembers(gitHubOrg string) []member { + client, err := gh.RESTClient(nil) + if err != nil { + log.Fatalf("failed to create REST client: %v", err) + } + p := 1 var members []member + for { - client := &http.Client{} - req, err := http.NewRequest( - "GET", - fmt.Sprintf("%s/%s/members?filter=all&page=%d", gitHubOrgAPI, o.GitHubOrg, p), - nil, - ) - if err != nil { - log.Fatal().Err(err) - } - req.Header.Add("authorization", fmt.Sprintf("token %s", gitHubToken)) - res, err := client.Do(req) - if err != nil { - log.Fatal().Err(err) - } - defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Fatal().Err(err) - } var ms []member - err = json.Unmarshal(body, &ms) + + err = client.Get(fmt.Sprintf("orgs/%s/members?page=%d&per_page=100", gitHubOrg, p), &ms) if err != nil { - log.Fatal().Err(err) + log.Fatalf("failed to get members for [%s]: %v", gitHubOrg, err) } + if len(ms) != 0 { members = append(members, ms...) p++ @@ -303,20 +290,48 @@ func (o cliOptions) getMembers() []member { break } } + return members } +// NewCommand is the root command of gh-vanity. +func NewCommand() *cobra.Command { + var ( + gitHubOrg string + showUsers string + ) + + var command = &cobra.Command{ + Use: "gh-audit-org-keys", + Short: "Audit GitHub organization keys.", + DisableAutoGenTag: true, + Version: Version, + RunE: func(cmd *cobra.Command, args []string) error { + members := getMembers(gitHubOrg) + keys := getKeys(members) + table := generateKeyTable(showUsers, keys) + printReport(table) + + return nil + }, + } + + command.Flags().StringVarP(&gitHubOrg, "organization", "o", "", "GitHub organization provided to inspect.") + command.Flags().StringVarP(&showUsers, "show-users", "s", "", "Display users with filter (`all`, `with`, `without`, `multiple`).") + + err := command.MarkFlagRequired("organization") + if err != nil { + return nil + } + + return command +} + func main() { - o := new(cliOptions) - pflag.StringVarP(&o.GitHubOrg, "organization", "o", "", "[required] GitHub organization provided to inspect") - pflag.StringVarP(&o.ShowUsers, "show-users", "s", "", "display users with filter (`all`, `with`, `without`, `multiple`)") - pflag.Parse() - if len(pflag.Args()) == 0 { - pflag.PrintDefaults() + command := NewCommand() + + if err := command.Execute(); err != nil { + fmt.Println(err) os.Exit(1) } - members := o.getMembers() - keys := getKeys(members) - table := o.generateKeyTable(keys) - printReport(table) }