Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
50afafb
chore(deps): bump actions/upload-artifact from 6 to 7
dependabot[bot] Mar 2, 2026
3871de3
chore(deps): bump github.com/go-git/go-git/v5 from 5.16.5 to 5.17.0
dependabot[bot] Mar 2, 2026
d7f3e48
chore(deps): bump actions/download-artifact from 7 to 8
dependabot[bot] Mar 2, 2026
a644e65
chore(deps): bump anchore/sbom-action from 0.22.2 to 0.23.0
dependabot[bot] Mar 2, 2026
57a11c1
Merge branch 'main' into dependabot/github_actions/anchore/sbom-actio…
yiftach-armis Mar 5, 2026
5516238
Merge branch 'main' into dependabot/go_modules/github.com/go-git/go-g…
yiftach-armis Mar 5, 2026
1b337e7
Merge branch 'main' into dependabot/github_actions/actions/download-a…
yiftach-armis Mar 5, 2026
b68c279
Merge branch 'main' into dependabot/github_actions/actions/upload-art…
yiftach-armis Mar 5, 2026
dca1cd4
chore(deps): bump github.com/mattn/go-runewidth from 0.0.20 to 0.0.21
dependabot[bot] Mar 9, 2026
727e8b3
chore(deps): bump golang.org/x/term from 0.40.0 to 0.41.0
dependabot[bot] Mar 16, 2026
f84580d
chore(deps): bump anchore/sbom-action from 0.23.0 to 0.23.1
dependabot[bot] Mar 16, 2026
26d7c1b
chore(deps): bump anchore/sbom-action from 0.23.0 to 0.24.0
dependabot[bot] Mar 23, 2026
9aa3827
[PPSC-602] fix: document CLI error info exposure as acceptable (CWE-209)
shb7628 Mar 23, 2026
9407382
[PPSC-602] fix: document progress bar size as display-only (CWE-770)
shb7628 Mar 23, 2026
bc9d504
[PPSC-602] fix: document spinner loop exit conditions (CWE-835)
shb7628 Mar 23, 2026
003420c
[PPSC-602] fix: truncate and label debug auth output (CWE-215)
shb7628 Mar 23, 2026
36d2be6
[PPSC-602] fix: document region cache credential storage (CWE-522)
shb7628 Mar 23, 2026
9284549
[PPSC-602] fix: document HTTP client URL source as non-user-controlle…
shb7628 Mar 23, 2026
d17df61
[PPSC-602] fix: document JWT signature verification design decision (…
shb7628 Mar 23, 2026
31418e3
[PPSC-602] fix: add character allowlist for image name validation (CW…
shb7628 Mar 23, 2026
93e6a39
[PPSC-602] fix: document path traversal protection for tarball input …
shb7628 Mar 23, 2026
1cef01b
[PPSC-602] fix: document existing page limit bounds validation (CWE-770)
shb7628 Mar 23, 2026
b44d8c9
[PPSC-602] fix: document path validation in loadSnippetFromFile (CWE-22)
shb7628 Mar 23, 2026
95cea09
[PPSC-602] fix: document GetRawToken security design decision (CWE-522)
shb7628 Mar 23, 2026
88336fc
[PPSC-602] fix: document intentionally discarded fmt.Fprintf returns …
shb7628 Mar 23, 2026
bd12c79
[PPSC-602] fix: validate install directory before PATH modification (…
shb7628 Mar 23, 2026
272a93b
[PPSC-602] fix: add warning before printing token to stdout (CWE-522)
shb7628 Mar 23, 2026
d43701e
[PPSC-602] fix: reject scan paths outside repository root (CWE-22)
shb7628 Mar 23, 2026
a2adefc
[PPSC-602] fix: add upper bound to scan and upload timeouts (CWE-770)
shb7628 Mar 23, 2026
3a23bc0
[PPSC-602] fix: add top-level read-only permissions to workflow (CKV2…
shb7628 Mar 23, 2026
5356bdd
[PPSC-602] fix: abort install when checksum tools unavailable (CWE-494)
shb7628 Mar 23, 2026
ddc1801
[PPSC-602] fix: document intentionally discarded stderr write (CWE-252)
shb7628 Mar 23, 2026
fc8ebde
[PPSC-602] fix: document intentionally discarded cursor control write…
shb7628 Mar 23, 2026
86b8561
[PPSC-602] fix: add size limit for .armisignore file reads (CWE-770)
shb7628 Mar 23, 2026
2a5ca60
[PPSC-602] fix: resolve symlinks to mitigate TOCTOU in SafeJoinPath (…
shb7628 Mar 23, 2026
0aa9935
[PPSC-602] fix: document test auth provider as false positive (CWE-522)
shb7628 Mar 23, 2026
a4c87cb
[PPSC-602] fix: document help buffer bounded by fixed command set (CW…
shb7628 Mar 23, 2026
f20facb
[PPSC-602] fix: validate install directory against allowed paths (CWE…
shb7628 Mar 23, 2026
98e991b
[PPSC-602] fix: guard against integer overflow in calculateFilesSize …
shb7628 Mar 23, 2026
9bcdcc0
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe20-improper-inpu…
shb7628 Mar 23, 2026
2c7a7c5
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe209-error-info-e…
shb7628 Mar 23, 2026
7e27d7f
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe215-debug-info-e…
shb7628 Mar 23, 2026
f5b6a4d
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe22-path-traversa…
shb7628 Mar 23, 2026
f055269
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe22-path-traversa…
shb7628 Mar 23, 2026
eb98429
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe22-path-traversa…
shb7628 Mar 23, 2026
cfd3d24
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe22-path-traversa…
shb7628 Mar 23, 2026
f877449
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe252-unchecked-fp…
shb7628 Mar 23, 2026
5ecac74
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe253-unchecked-fp…
shb7628 Mar 23, 2026
11d66e3
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe253-unchecked-fp…
shb7628 Mar 23, 2026
525931d
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe327-jwt-no-signa…
shb7628 Mar 23, 2026
ee18567
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe367-toctou-race'…
shb7628 Mar 23, 2026
bb0314b
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe427-uncontrolled…
shb7628 Mar 23, 2026
7869cf3
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe494-download-no-…
shb7628 Mar 23, 2026
cc95a45
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe522-creds-in-cac…
shb7628 Mar 23, 2026
fa71583
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe522-raw-token-re…
shb7628 Mar 23, 2026
c4319c9
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe522-test-auth-he…
shb7628 Mar 23, 2026
24653ce
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe522-token-printe…
shb7628 Mar 23, 2026
55dcac8
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe770-unbounded-he…
shb7628 Mar 23, 2026
571ef4b
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe770-unbounded-ig…
shb7628 Mar 23, 2026
cda103c
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe770-unbounded-pa…
shb7628 Mar 23, 2026
32fab0c
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe770-unbounded-pr…
shb7628 Mar 23, 2026
8df6d72
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe770-unbounded-sc…
shb7628 Mar 23, 2026
bf39ad0
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe835-infinite-loo…
shb7628 Mar 23, 2026
4204a96
Merge remote-tracking branch 'origin/fix/PPSC-602-cwe918-ssrf-httpcli…
shb7628 Mar 23, 2026
40a3ad9
Merge remote-tracking branch 'origin/fix/PPSC-602-gha-workflow-permis…
shb7628 Mar 23, 2026
f350496
Merge remote-tracking branch 'origin/dependabot/github_actions/action…
shb7628 Mar 23, 2026
3b1c4dd
Merge remote-tracking branch 'origin/dependabot/github_actions/action…
shb7628 Mar 23, 2026
6519d38
Merge remote-tracking branch 'origin/dependabot/github_actions/anchor…
shb7628 Mar 23, 2026
9bd7b0e
Merge remote-tracking branch 'origin/dependabot/github_actions/anchor…
shb7628 Mar 23, 2026
3b2b04e
Merge remote-tracking branch 'origin/dependabot/go_modules/github.com…
shb7628 Mar 23, 2026
31590f5
Merge remote-tracking branch 'origin/dependabot/go_modules/github.com…
shb7628 Mar 23, 2026
4bb05ab
Merge remote-tracking branch 'origin/dependabot/go_modules/golang.org…
shb7628 Mar 23, 2026
99858ac
Merge dependabot/github_actions/anchore/sbom-action-0.24.0 (resolve c…
shb7628 Mar 23, 2026
9ae4cf6
Merge main into mega branch, resolve install.ps1 conflict
shb7628 Mar 23, 2026
4c7d0d4
chore: update CI workflows to Go 1.25 for dependabot module updates
shb7628 Mar 23, 2026
cc954a6
fix: update path tests for EvalSymlinks in SafeJoinPath
shb7628 Mar 23, 2026
cd5e580
fix: address 4 additional security scan findings
shb7628 Mar 23, 2026
63c9f88
ci: trigger security scan on pull requests
shb7628 Mar 23, 2026
3236af9
fix: address 15 remaining security scan findings
shb7628 Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
go-version: "1.25"
cache: true

- name: Install gotestsum
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
go-version: "1.25"
cache: true

- name: Install Syft
uses: anchore/sbom-action/download-syft@v0.23.0
uses: anchore/sbom-action/download-syft@v0.24.0

- name: Run GoReleaser (Snapshot)
uses: goreleaser/goreleaser-action@v7
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
go-version: "1.25"
cache: true

- name: Install gotestsum
Expand Down Expand Up @@ -63,11 +63,11 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
go-version: "1.25"
cache: true

- name: Install Syft
uses: anchore/sbom-action/download-syft@v0.23.0
uses: anchore/sbom-action/download-syft@v0.24.0

- name: Install cosign
uses: sigstore/cosign-installer@v3
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/reusable-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ name: Reusable Lint
on:
workflow_call:

# CKV2_GHA_1: Restrict top-level permissions to read-only (least privilege)
permissions:
contents: read

jobs:
lint:
name: Lint
Expand All @@ -16,7 +20,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.24"
go-version: "1.25"
cache: true

- name: Go fmt check
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Security Scan

on:
pull_request:
branches: [main]
workflow_dispatch: # Manual trigger
schedule:
- cron: '0 6 * * *' # Daily at 06:00 UTC
Expand Down
11 changes: 5 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
module github.com/ArmisSecurity/armis-cli

go 1.24.0
go 1.25.0

require (
github.com/alecthomas/chroma/v2 v2.23.1
github.com/cenkalti/backoff/v4 v4.3.0
github.com/charmbracelet/lipgloss v1.1.0
github.com/distribution/reference v0.6.0
github.com/go-git/go-git/v5 v5.17.0
github.com/mattn/go-runewidth v0.0.20
github.com/mattn/go-runewidth v0.0.21
github.com/muesli/termenv v0.16.0
github.com/schollz/progressbar/v3 v3.19.0
github.com/spf13/cobra v1.10.2
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/term v0.40.0
golang.org/x/term v0.41.0
)

require (
Expand All @@ -22,8 +22,7 @@ require (
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.8.0 // indirect
Expand All @@ -39,6 +38,6 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/sys v0.42.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
18 changes: 8 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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=
Expand Down Expand Up @@ -52,8 +50,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ=
github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
Expand Down Expand Up @@ -94,10 +92,10 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbR
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=
Expand Down
13 changes: 9 additions & 4 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,12 @@
// specifically for the `auth` command to output tokens for piping to other tools.
// The token is only printed to stdout when explicitly requested by the user.
//
// #nosec G101 -- Intentional credential exposure for CLI output
// #nosec G101 CWE-522 -- Intentional credential exposure for CLI output.
// Raw token access is required for the `auth` command and for setting Authorization headers.
// The CLI runs in the user's own security context; token visibility is by design.
func (p *AuthProvider) GetRawToken(ctx context.Context) (string, error) {
if p.isLegacy {
return p.config.Token, nil
return p.config.Token, nil // #nosec CWE-522 -- intentional token return for CLI auth command

Check failure

Code scanning / Armis Security Scanner

Insecure Design (CWE-522: Insufficiently Protected Credentials) High

Insecure Design (CWE-522: Insufficiently Protected Credentials): The GetRawToken method returns the raw authentication token stored in p.config.Token when legacy (Basic) authentication is used. This value originates from configuration supplied by the user (e.g., command‑line flag or environment variable) and is passed directly to the caller without any masking, encryption, or additional protection. Consequently, the method exposes sensitive credentials in clear text, matching the definition of CWE‑522: Insufficiently Protected Credentials. The token can be accessed by any code that invokes this method, and because the function may be used to print the token to stdout, it can be observed by other processes on the same host. The vulnerability is confined to internal script‑to‑script interaction, giving it an exposure level of 5 and a low likelihood of exploitation in the absence of external access, but it still constitutes a true positive for the reported CWE.
}

// Refresh JWT if needed
Expand Down Expand Up @@ -293,15 +295,18 @@
// 2. The backend validates the signature server-side for all API requests
// 3. This function only extracts claims for local caching/refresh logic
//
// #nosec G104 -- JWT signature verification delegated to backend
// #nosec G104 CWE-327 -- JWT signature verification is intentionally omitted.
// The CLI is the consumer (not validator) of tokens received via HTTPS from the auth service.
// Server-side validation occurs on every API request. Claims here are used only for
// client-side caching, refresh logic, and region routing.
func parseJWTClaims(token string) (*jwtClaims, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid JWT format: expected 3 parts, got %d", len(parts))
}

// Decode payload (second part) - JWT uses base64url encoding without padding
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
payload, err := base64.RawURLEncoding.DecodeString(parts[1]) // #nosec CWE-327 -- decode only, no crypto verification needed

Check failure

Code scanning / Armis Security Scanner

Cryptography Failures (CWE-327: Use of Broken or Risky Cryptographic Algorithm) High

Cryptography Failures (CWE-327: Use of Broken or Risky Cryptographic Algorithm): The function parseJWTClaims (line 309) decodes the JWT payload without verifying the signature. This omission means the code trusts data that could be forged, matching CWE‑327 (use of a risky cryptographic practice). The JWT token originates from an external authentication service accessed over HTTP, so an attacker who can influence that response could supply a malicious token that the CLI would accept without validation. The vulnerable decode operation is reachable through the token flow: exchangeCredentials obtains the token via AuthClient.Authenticate and passes it to parseJWTClaims. Because the flaw is in the same functional flow as line 309 and directly concerns missing signature verification, the finding is a true positive. The exposure is rated 6 since the data comes from a network‑accessible service, leading to a high likelihood of exploitation.
if err != nil {
return nil, fmt.Errorf("failed to decode JWT payload: %w", err)
}
Expand Down
6 changes: 3 additions & 3 deletions internal/auth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@

req.Header.Set("Content-Type", "application/json")

resp, err := c.httpClient.Do(req) //nolint:gosec // G704: authEndpoint is constructed from validated config, not user input
resp, err := c.httpClient.Do(req) //nolint:gosec // CWE-522,G704: credentials sent over HTTPS to validated endpoint

Check failure

Code scanning / Armis Security Scanner

Insecure Design (CWE-522: Insufficiently Protected Credentials) High

Insecure Design (CWE-522: Insufficiently Protected Credentials): The Authenticate function builds a JSON body that includes the client secret and sends it with an HTTP POST request. The client enforces HTTPS only for non‑localhost URLs; for localhost it permits plain HTTP. Consequently, when the base URL is an insecure HTTP address (e.g., http://localhost), the secret is transmitted without additional protection beyond the transport layer, matching CWE‑522. The secret originates from the function arguments, flows through json.Marshal, and reaches the network request made by httpClient.Do, with no sanitization in between. This creates a reachable taint path that can expose credentials to anyone able to intercept the local network traffic. The vulnerability is exposed through a network‑accessible authentication endpoint, giving it an exposure rating of 6 and a high likelihood of exploitation.
if err != nil {
return nil, fmt.Errorf("authentication request failed: %w", err)
}
Expand All @@ -125,9 +125,9 @@
}

if resp.StatusCode != http.StatusOK {
// Log detailed error info when debug mode is enabled
// Log error metadata when debug mode is enabled (body omitted to avoid credential leakage).
if c.debug {
fmt.Fprintf(os.Stderr, "DEBUG: Auth failed with status %d, body: %s\n", resp.StatusCode, string(body))
fmt.Fprintf(os.Stderr, "[DEBUG] Auth failed with status %d, response length: %d bytes\n", resp.StatusCode, len(body))
}
// Don't include raw response body in error to prevent potential info leakage
return nil, &AuthError{StatusCode: resp.StatusCode, Message: fmt.Sprintf("authentication failed (status %d)", resp.StatusCode)}
Expand Down
3 changes: 3 additions & 0 deletions internal/auth/region_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ func (c *RegionCache) Save(clientID, region string) {
return
}

// CWE-522 false positive: clientID is an identifier, not a secret credential.
// File permissions 0o600 restrict access to the owning user. This is standard
// practice for CLI config caches (similar to ~/.docker/config.json).
_ = os.WriteFile(path, data, 0o600) //nolint:gosec // path validated by getFilePath
}

Expand Down
8 changes: 6 additions & 2 deletions internal/cmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import (
"context"
"fmt"
"os"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -58,7 +59,10 @@
return fmt.Errorf("failed to get token: %w", err)
}

// Print the raw token without any prefix (useful for piping to other tools)
fmt.Println(token)
// Print the raw token without any prefix (useful for piping to other tools).
// CWE-522: Token output is the intentional purpose of this command.
// Warning is sent to stderr so it doesn't interfere with piped usage.
fmt.Fprintln(os.Stderr, "Warning: token output below. Avoid storing in logs or shell history.")
fmt.Println(token) // #nosec CWE-522 -- intentional token output for CLI auth command

Check failure

Code scanning / Armis Security Scanner

Insecure Design (CWE-522: Insufficiently Protected Credentials) High

Insecure Design (CWE-522: Insufficiently Protected Credentials): The runAuth function obtains a JWT token via provider.GetRawToken and then prints the raw token directly to standard output with fmt.Println(token). This output can be captured by shell history, logs, or other processes, exposing the credential without protection. The token originates from a sensitive source (the authentication provider) and reaches an insecure sink (stdout) without any sanitization or masking, making the credential reachable to an attacker who can observe the CLI output. Because the command is a CLI tool, the exposure is classified as script-to-script interaction (level 5), leading to a low likelihood of exploitation but a high severity due to the nature of the leaked credential.
return nil
}
6 changes: 4 additions & 2 deletions internal/cmd/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
func handleScanError(ctx context.Context, err error) error {
_ = ctx // unused but kept for API consistency
if errors.Is(err, context.Canceled) {
_, _ = fmt.Fprintln(os.Stderr, "") // newline before warning; ignore write errors
// CWE-252 false positive: write errors for stderr formatting are intentionally
// discarded - no meaningful recovery for failed terminal writes.
_, _ = fmt.Fprintln(os.Stderr, "")

Check notice

Code scanning / Armis Security Scanner

The function `handleScanError` calls `fmt.Fprintln(os.Stderr, "")` and discards both the number of bytes written and the error return value Low

The function handleScanError calls fmt.Fprintln(os.Stderr, "") and discards both the number of bytes written and the error return value: The function handleScanError calls fmt.Fprintln(os.Stderr, "") and discards both the number of bytes written and the error return value. Ignoring the error means that a failure to write to the standard error stream (e.g., when stderr is redirected to a closed pipe or a full disk) will go unnoticed, and the program will continue without informing the user that the cancellation message could not be displayed. This matches CWE‑252: Unchecked Return Value. The code does not involve any external input or network exposure; it operates locally within a CLI tool, so the vulnerability is considered low exposure (static, internal usage). Consequently, the likelihood of exploitation is low, but the issue is a true positive because the unchecked return value is present.
cli.PrintWarning("Scan cancelled")
return ErrScanCancelled
}
return fmt.Errorf("scan failed: %w", err)
return fmt.Errorf("scan failed: %w", err) // #nosec CWE-209 -- CLI displays errors to local user only

Check failure

Code scanning / Armis Security Scanner

Insecure Design (CWE-209: Information Exposure Through an Error Message) High

Insecure Design (CWE-209: Information Exposure Through an Error Message): The function handleScanError returns a wrapped error with the original error message included (fmt.Errorf("scan failed: %w", err)). This error is later presented to the CLI user, revealing internal details about why the scan failed. Even though the comment notes that the output is only shown to a local user, the CLI is a user‑facing interface, so the information exposure is immediate and external. An attacker or any user running the tool can see internal error information that may aid in further exploitation or debugging of the system. No sanitization is applied to the original error before it is included in the message, making the exposure reachable. Consequently, the finding matches CWE‑209 and is a true positive.
}
4 changes: 3 additions & 1 deletion internal/cmd/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ func SetupHelp(cmd *cobra.Command) {
// Save original output destination before redirecting
originalOut := c.OutOrStdout()

// Capture help output to a buffer
// CWE-770 false positive: buffer content is from Cobra's help template
// (hardcoded command names/flags), not user input. Additionally, styleHelpOutput
// enforces maxHelpTextSize as defense-in-depth.
buf := new(bytes.Buffer)
c.SetOut(buf)
c.SetUsageTemplate(styledUsageTemplate())
Expand Down
10 changes: 9 additions & 1 deletion internal/cmd/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,21 @@ var scanCmd = &cobra.Command{
return fmt.Errorf("invalid --exit-code value %d: must be between 1 and 255", exitCode)
}

// Validate timeout values: must be positive
// Validate timeout values: must be positive and bounded
if scanTimeout < 1 {
return fmt.Errorf("invalid --scan-timeout value %d: must be at least 1 minute", scanTimeout)
}
const maxScanTimeout = 1440 // 24 hours
if scanTimeout > maxScanTimeout {
return fmt.Errorf("invalid --scan-timeout value %d: must not exceed %d minutes (24 hours)", scanTimeout, maxScanTimeout)
}
if uploadTimeout < 1 {
return fmt.Errorf("invalid --upload-timeout value %d: must be at least 1 minute", uploadTimeout)
}
const maxUploadTimeout = 120 // 2 hours
if uploadTimeout > maxUploadTimeout {
return fmt.Errorf("invalid --upload-timeout value %d: must not exceed %d minutes", uploadTimeout, maxUploadTimeout)
}

return nil
},
Expand Down
4 changes: 3 additions & 1 deletion internal/cmd/scan_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
}

if tarballPath != "" {
// CWE-22: SanitizePath rejects ".." path components and cleans the path.
// The tarball path comes from a CLI flag (local user context, not remote input).
sanitizedPath, pathErr := util.SanitizePath(tarballPath)
if pathErr != nil {
return fmt.Errorf("invalid tarball path: %w", pathErr)
Expand All @@ -115,7 +117,7 @@
}
} else {
imageName := args[0]
result, err = scanner.ScanImage(ctx, imageName)
result, err = scanner.ScanImage(ctx, imageName) // #nosec CWE-20 -- validated by validateImageName above

Check failure

Code scanning / Armis Security Scanner

Injection (CWE-20: Improper Input Validation) High

Injection (CWE-20: Improper Input Validation): The command-line argument args[0] (the image name) is taken directly from user input and passed to scanner.ScanImage without any validation or sanitization in the shown code. This constitutes improper input validation (CWE-20). An attacker who can control the CLI arguments can supply a crafted image name that may trigger unexpected behavior in the scanning component, potentially leading to command injection or other misuse depending on how ScanImage processes the name. The taint source (CLI argument) reaches the sink (ScanImage) with no intervening sanitizers, making the vulnerability exploitable. Because the tool is invoked via a user‑facing command-line interface, the exposure level is classified as high (≥6), resulting in a high likelihood of exploitation.
if err != nil {
return handleScanError(ctx, err)
}
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/scan_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ var scanRepoCmd = &cobra.Command{
return err
}

// CWE-770 false positive: getPageLimit() validates page limit is in range 1-1000
// (see validatePageLimit in root.go). The limit is already bounded.
limit, err := getPageLimit()
if err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion internal/httpclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@
req.Body = newBody
}

resp, err = c.httpClient.Do(req) //nolint:gosec // G704: URL is from API client, validated before use
// CWE-918 false positive: URLs are constructed internally by the API client from
// validated configuration (base URL + fixed endpoints), not from user input.
// The base URL is validated in NewAuthClient (HTTPS required for non-localhost).
resp, err = c.httpClient.Do(req) //nolint:gosec // G704: URL from API client, not user-controlled

Check failure

Code scanning / Armis Security Scanner

Server-Side Request Forgery (CWE-918: Server Side Request Forgery (SSRF)) High

Server-Side Request Forgery (CWE-918: Server Side Request Forgery (SSRF)): The Do method accepts an *http.Request and forwards it directly to c.httpClient.Do(req) without any validation or sanitization of the request's URL. If an attacker can influence the URL field of the request—such as through an upstream API endpoint that builds the request from user‑supplied data—the server will issue an arbitrary outbound HTTP request. This matches the definition of CWE‑918 (Server Side Request Forgery). No sanitizing functions are applied in the data flow, so the tainted URL reaches the network request sink. The function is part of an internal client library, so the issue is not directly exposed to the public internet but can be triggered by any component that passes a request to it, giving it a moderate likelihood of exploitation.
if err != nil {
// Close response body if present to prevent resource leaks
// (some HTTP errors may return both a response and an error)
Expand Down
6 changes: 4 additions & 2 deletions internal/output/human.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ func loadSnippetFromFile(repoPath string, finding model.Finding) (snippet string
if filepath.IsAbs(finding.File) {
return "", 0, fmt.Errorf("absolute path not allowed without repository context: %q", finding.File)
}
// CWE-22: SanitizePath rejects ".." path components to prevent traversal.
// With repoPath, SafeJoinPath (above) ensures containment within the repo.
sanitized, pathErr := util.SanitizePath(finding.File)
if pathErr != nil {
return "", 0, fmt.Errorf("invalid file path: %w", pathErr)
Expand Down Expand Up @@ -1005,7 +1007,7 @@ func severityRank(sev model.Severity) int {
}

func isGitRepo(repoPath string) bool {
cmd := exec.Command("git", "rev-parse", "--is-inside-work-tree")
cmd := exec.Command("git", "rev-parse", "--is-inside-work-tree") // #nosec CWE-22 -- repoPath from CLI arg (local user context)
cmd.Dir = repoPath
err := cmd.Run()
return err == nil
Expand Down Expand Up @@ -1035,7 +1037,7 @@ func getGitBlame(repoPath, file string, line int, debug bool) *GitBlameInfo {

// #nosec G204 -- file path is validated above, git blame is intentional for showing code ownership
// Use "--" separator to prevent file argument from being interpreted as an option
cmd := exec.Command("git", "blame", "-L", fmt.Sprintf("%d,%d", line, line), "--porcelain", "--", file)
cmd := exec.Command("git", "blame", "-L", fmt.Sprintf("%d,%d", line, line), "--porcelain", "--", file) // #nosec CWE-22
cmd.Dir = repoPath
output, err := cmd.Output()
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions internal/progress/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ func NewReader(r io.Reader, size int64, description string, disabled bool) io.Re
return r
}

// CWE-770 false positive: size is used only for progress percentage display,
// not for memory allocation. progressbar.DefaultBytes does not allocate size bytes.
bar := progressbar.DefaultBytes(
size,
description,
Expand Down Expand Up @@ -176,6 +178,8 @@ func (s *Spinner) Start() {
s.mu.Unlock()

if s.disabled || IsCI() {
// CWE-253 false positive: write errors for terminal/CI output are intentionally
// discarded - there is no meaningful recovery action for failed terminal writes.
_, _ = fmt.Fprintf(s.writer, "%s (started at %s)\n", message, startTime.Format("15:04:05"))
return
}
Expand Down Expand Up @@ -215,6 +219,8 @@ func (s *Spinner) Start() {
// even when --color=never. This matches clearLine() which uses \033[K.
hideCursor := isTerminalWriter(s.writer)
if hideCursor {
// CWE-253 false positive: write errors for terminal cursor control sequences
// are intentionally discarded - no meaningful recovery for failed terminal writes.
_, _ = fmt.Fprint(s.writer, cursorHide)
defer func() { _, _ = fmt.Fprint(s.writer, cursorShow) }()
}
Expand All @@ -232,6 +238,8 @@ func (s *Spinner) Start() {
return "\r\033[K"
}

// CWE-835 false positive: loop exits via stopChan (Stop()), ctx.Done
// (context cancel/timeout), or safety-net DefaultSpinnerTimeout (30 min).
for {
select {
case <-s.stopChan:
Expand Down
Loading
Loading