Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 40 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 10 * * 1' # Weekly on Mondays at 10 AM UTC

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
security-events: write

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Initialize CodeQL
uses: github/codeql-action/init@4f3212b61783c3c68e8309a0f18a699764811cda # v3.28.1
with:
languages: go
queries: security-extended

- name: Autobuild
uses: github/codeql-action/autobuild@4f3212b61783c3c68e8309a0f18a699764811cda # v3.28.1

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4f3212b61783c3c68e8309a0f18a699764811cda # v3.28.1
with:
category: "/language:go"
13 changes: 10 additions & 3 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Run Gosec
uses: securego/gosec@424fc4cd9c82ea0fd6bee9cd49c2db2c3cc0c93f # v2.22.11
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
args: ./...
go-version: '1.25'
cache: true

- name: Install Gosec
run: curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.22.11

- name: Run Gosec
run: gosec ./...

govulncheck:
name: Go Vulnerability Check
Expand Down
5 changes: 3 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ linters:
- path: _test\.go
text: "unused-parameter"

max-issues-per-linter: 50
max-same-issues: 10
issues:
max-issues-per-linter: 50
max-same-issues: 10

formatters:
enable:
Expand Down
10 changes: 10 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/pyre/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func main() {
var (
host = flag.String("host", "", "Firewall hostname or IP address")
apiKey = flag.String("api-key", "", "API key for authentication")
insecure = flag.Bool("insecure", true, "Skip TLS certificate verification")
insecure = flag.Bool("insecure", false, "Skip TLS certificate verification (for self-signed certs)")
configPath = flag.String("config", "", "Path to config file (default: ~/.pyre.yaml)")
showHelp = flag.Bool("help", false, "Show help message")
showVer = flag.Bool("version", false, "Show version")
Expand Down
13 changes: 6 additions & 7 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func WithInsecure(insecure bool) ClientOption {
return func(c *Client) {
if insecure {
c.httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // #nosec G402 -- InsecureSkipVerify required for self-signed firewall certificates when user enables --insecure
}
}
}
Expand All @@ -44,9 +44,6 @@ func NewClient(host, apiKey string, opts ...ClientOption) *Client {
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
},
}

Expand Down Expand Up @@ -104,8 +101,6 @@ func (c *Client) GetTarget() string {
}

func (c *Client) request(ctx context.Context, params url.Values) (*XMLResponse, error) {
params.Set("key", c.apiKey)

// Inject target parameter for Panorama routing
if c.targetSerial != "" {
params.Set("target", c.targetSerial)
Expand All @@ -117,11 +112,15 @@ func (c *Client) request(ctx context.Context, params url.Values) (*XMLResponse,
return nil, fmt.Errorf("creating request: %w", err)
}

// Use X-PAN-KEY header instead of query parameter (PAN-OS 8.0+)
// This prevents API key from appearing in server/proxy logs
req.Header.Set("X-PAN-KEY", c.apiKey)

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("executing request: %w", err)
}
defer resp.Body.Close()
defer func() { _ = resp.Body.Close() }() //nolint:errcheck // best effort cleanup

body, err := io.ReadAll(resp.Body)
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions internal/api/monitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (c *Client) GetJobs(ctx context.Context) ([]models.Job, error) {

// Parse progress - ignore error, zero value acceptable for non-numeric progress
if e.Progress != "" {
job.Progress, _ = strconv.Atoi(strings.TrimSuffix(e.Progress, "%"))
job.Progress, _ = strconv.Atoi(strings.TrimSuffix(e.Progress, "%")) //nolint:errcheck // intentional - default to 0 on parse error
}

// Parse timestamps - PAN-OS typically uses format like "2024/01/15 10:30:45"
Expand Down Expand Up @@ -248,7 +248,7 @@ func (c *Client) GetDiskUsage(ctx context.Context) ([]models.DiskUsage, error) {
fields := strings.Fields(line)
if len(fields) >= 6 {
pctStr := strings.TrimSuffix(fields[4], "%")
pct, _ := strconv.ParseFloat(pctStr, 64)
pct, _ := strconv.ParseFloat(pctStr, 64) //nolint:errcheck // intentional - default to 0 on parse error

disk := models.DiskUsage{
Filesystem: fields[0],
Expand All @@ -266,6 +266,8 @@ func (c *Client) GetDiskUsage(ctx context.Context) ([]models.DiskUsage, error) {
}

// GetEnvironmentals retrieves hardware environmental sensor data
//
//nolint:misspell // "environmentals" is the PAN-OS XML API tag name
func (c *Client) GetEnvironmentals(ctx context.Context) ([]models.Environmental, error) {
resp, err := c.Op(ctx, "<show><system><environmentals></environmentals></system></show>")
if err != nil {
Expand Down
28 changes: 14 additions & 14 deletions internal/api/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ func (c *Client) GetSecurityPolicies(ctx context.Context) ([]models.SecurityRule
if err != nil {
return nil, err
}
if err := CheckResponse(resp); err != nil {
return nil, err
if checkErr := CheckResponse(resp); checkErr != nil {
return nil, checkErr
}

// Handle empty result
Expand Down Expand Up @@ -116,14 +116,14 @@ func (c *Client) GetSecurityPolicies(ctx context.Context) ([]models.SecurityRule
var withWrapper struct {
Entry []ruleEntry `xml:"rules>entry"`
}
if err := xml.Unmarshal(WrapInner(resp.Result.Inner), &withWrapper); err == nil && len(withWrapper.Entry) > 0 {
if unmarshalErr := xml.Unmarshal(WrapInner(resp.Result.Inner), &withWrapper); unmarshalErr == nil && len(withWrapper.Entry) > 0 {
entries = withWrapper.Entry
} else {
// Try parsing without wrapper (entries directly in result)
var withoutWrapper struct {
Entry []ruleEntry `xml:"entry"`
}
if err := xml.Unmarshal(WrapInner(resp.Result.Inner), &withoutWrapper); err == nil {
if unmarshalErr := xml.Unmarshal(WrapInner(resp.Result.Inner), &withoutWrapper); unmarshalErr == nil {
entries = withoutWrapper.Entry
}
}
Expand Down Expand Up @@ -230,17 +230,17 @@ func (c *Client) GetSecurityPolicies(ctx context.Context) ([]models.SecurityRule
for _, h := range hitResult.Entry {
stats := hitStats{count: h.HitCount}
if h.LastHit != "" && h.LastHit != "0" {
if ts, _ := strconv.ParseInt(h.LastHit, 10, 64); ts > 0 {
if ts, _ := strconv.ParseInt(h.LastHit, 10, 64); ts > 0 { //nolint:errcheck // intentional - default to zero time on parse error
stats.lastHit = time.Unix(ts, 0)
}
}
if h.FirstHit != "" && h.FirstHit != "0" {
if ts, _ := strconv.ParseInt(h.FirstHit, 10, 64); ts > 0 {
if ts, _ := strconv.ParseInt(h.FirstHit, 10, 64); ts > 0 { //nolint:errcheck // intentional - default to zero time on parse error
stats.firstHit = time.Unix(ts, 0)
}
}
if h.LastReset != "" && h.LastReset != "0" {
if ts, _ := strconv.ParseInt(h.LastReset, 10, 64); ts > 0 {
if ts, _ := strconv.ParseInt(h.LastReset, 10, 64); ts > 0 { //nolint:errcheck // intentional - default to zero time on parse error
stats.lastReset = time.Unix(ts, 0)
}
}
Expand Down Expand Up @@ -286,8 +286,8 @@ func (c *Client) GetNATRules(ctx context.Context) ([]models.NATRule, error) {
if err != nil {
return nil, err
}
if err := CheckResponse(resp); err != nil {
return nil, err
if checkErr := CheckResponse(resp); checkErr != nil {
return nil, checkErr
}

// Handle empty result
Expand Down Expand Up @@ -357,14 +357,14 @@ func (c *Client) GetNATRules(ctx context.Context) ([]models.NATRule, error) {
var withWrapper struct {
Entry []natEntry `xml:"rules>entry"`
}
if err := xml.Unmarshal(WrapInner(resp.Result.Inner), &withWrapper); err == nil && len(withWrapper.Entry) > 0 {
if unmarshalErr := xml.Unmarshal(WrapInner(resp.Result.Inner), &withWrapper); unmarshalErr == nil && len(withWrapper.Entry) > 0 {
entries = withWrapper.Entry
} else {
// Try parsing without wrapper
var withoutWrapper struct {
Entry []natEntry `xml:"entry"`
}
if err := xml.Unmarshal(WrapInner(resp.Result.Inner), &withoutWrapper); err == nil {
if unmarshalErr := xml.Unmarshal(WrapInner(resp.Result.Inner), &withoutWrapper); unmarshalErr == nil {
entries = withoutWrapper.Entry
}
}
Expand Down Expand Up @@ -443,17 +443,17 @@ func (c *Client) GetNATRules(ctx context.Context) ([]models.NATRule, error) {
for _, h := range hitResult.Entry {
stats := hitStats{count: h.HitCount}
if h.LastHit != "" && h.LastHit != "0" {
if ts, _ := strconv.ParseInt(h.LastHit, 10, 64); ts > 0 {
if ts, _ := strconv.ParseInt(h.LastHit, 10, 64); ts > 0 { //nolint:errcheck // intentional - default to zero time on parse error
stats.lastHit = time.Unix(ts, 0)
}
}
if h.FirstHit != "" && h.FirstHit != "0" {
if ts, _ := strconv.ParseInt(h.FirstHit, 10, 64); ts > 0 {
if ts, _ := strconv.ParseInt(h.FirstHit, 10, 64); ts > 0 { //nolint:errcheck // intentional - default to zero time on parse error
stats.firstHit = time.Unix(ts, 0)
}
}
if h.LastReset != "" && h.LastReset != "0" {
if ts, _ := strconv.ParseInt(h.LastReset, 10, 64); ts > 0 {
if ts, _ := strconv.ParseInt(h.LastReset, 10, 64); ts > 0 { //nolint:errcheck // intentional - default to zero time on parse error
stats.lastReset = time.Unix(ts, 0)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/api/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (c *Client) GetSessions(ctx context.Context, filter string) ([]models.Sessi
var startTime time.Time
// Ignore parse error - time format may vary, zero time acceptable
if e.StartTime != "" {
startTime, _ = time.Parse("Mon Jan 2 15:04:05 2006", e.StartTime)
startTime, _ = time.Parse("Mon Jan 2 15:04:05 2006", e.StartTime) //nolint:errcheck // intentional - zero time acceptable
}
// Convert protocol number to name
proto := protoToName(e.Proto)
Expand Down
10 changes: 5 additions & 5 deletions internal/api/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ func (c *Client) GetSystemResources(ctx context.Context) (*models.Resources, err
// Parse load average using regex
// Ignore parse errors - optional fields, zero value acceptable if parsing fails
if matches := loadAvgRegex.FindStringSubmatch(output); len(matches) >= 4 {
resources.Load1, _ = strconv.ParseFloat(matches[1], 64)
resources.Load5, _ = strconv.ParseFloat(matches[2], 64)
resources.Load15, _ = strconv.ParseFloat(matches[3], 64)
resources.Load1, _ = strconv.ParseFloat(matches[1], 64) //nolint:errcheck // intentional - zero value acceptable
resources.Load5, _ = strconv.ParseFloat(matches[2], 64) //nolint:errcheck // intentional - zero value acceptable
resources.Load15, _ = strconv.ParseFloat(matches[3], 64) //nolint:errcheck // intentional - zero value acceptable
}

lines := strings.Split(output, "\n")
Expand Down Expand Up @@ -278,10 +278,10 @@ func (c *Client) GetSystemResources(ctx context.Context) (*models.Resources, err
// Ignore parse errors - fields may have unexpected format, zero value acceptable
cleanField := strings.TrimRight(f, ",%")
if (cleanField == "total" || f == "total," || f == "total") && i > 0 {
total, _ = strconv.ParseFloat(strings.TrimRight(fields[i-1], ",%"), 64)
total, _ = strconv.ParseFloat(strings.TrimRight(fields[i-1], ",%"), 64) //nolint:errcheck // intentional
}
if (cleanField == "used" || f == "used," || f == "used") && i > 0 {
used, _ = strconv.ParseFloat(strings.TrimRight(fields[i-1], ",%"), 64)
used, _ = strconv.ParseFloat(strings.TrimRight(fields[i-1], ",%"), 64) //nolint:errcheck // intentional
}
}
if total > 0 {
Expand Down
Loading
Loading