@htekdev/actions-debugger 1.0.62 → 1.0.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ id: caching-artifacts-042
2
+ title: "Cache key containing github.sha always misses on restore — cache is saved but never reused"
3
+ category: caching-artifacts
4
+ severity: silent-failure
5
+ tags:
6
+ - cache
7
+ - cache-key
8
+ - github-sha
9
+ - cache-miss
10
+ - restore-keys
11
+ - performance
12
+ patterns:
13
+ - regex: 'Cache not found for input keys: .*\b[0-9a-f]{40}\b'
14
+ flags: i
15
+ - regex: 'Warning: Cache not found for.*sha.*\.'
16
+ flags: i
17
+ - regex: 'Cache restored from key: .*\b[0-9a-f]{40}\b'
18
+ flags: i
19
+ error_messages:
20
+ - "Cache not found for input keys: Linux-node-a3f2b1c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9"
21
+ - "Warning: Cache not found for key: ubuntu-latest-npm-abc1234def5678901234567890abcdef12345678"
22
+ root_cause: |
23
+ When a cache key includes ${{ github.sha }}, every commit produces a unique key. The cache is
24
+ saved successfully at the end of the workflow, but on the very next run the sha is different,
25
+ so the restore step finds no matching cache entry and declares a miss. This pattern ensures
26
+ the cache is written once and thrown away — it provides no performance benefit.
27
+
28
+ This mistake is common because github.sha is a readily available context value and its use
29
+ in cache keys looks reasonable at first glance. The cache action's restore-keys fallback
30
+ mechanism would normally rescue a miss, but developers often omit restore-keys when using
31
+ sha-based keys, expecting the primary key to match.
32
+ fix: |
33
+ Replace github.sha with a hash of the files that determine cache validity, such as
34
+ hashFiles('**/package-lock.json') for npm or hashFiles('**/go.sum') for Go. Use github.sha
35
+ only as a restore-keys fallback prefix if you want a per-commit cache layer on top of a
36
+ stable base cache. The cache key should be stable across commits unless the relevant
37
+ dependency manifest changes.
38
+ fix_code:
39
+ - language: yaml
40
+ label: "Correct cache key using hashFiles instead of github.sha"
41
+ code: |
42
+ - name: Cache node modules
43
+ uses: actions/cache@v4
44
+ with:
45
+ path: ~/.npm
46
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
47
+ restore-keys: |
48
+ ${{ runner.os }}-node-
49
+ - language: yaml
50
+ label: "Per-commit layer on top of a stable base cache (advanced)"
51
+ code: |
52
+ # Stable key hits on re-runs; sha layer saves per-commit installs
53
+ - name: Cache node modules
54
+ uses: actions/cache@v4
55
+ with:
56
+ path: ~/.npm
57
+ key: ${{ runner.os }}-node-${{ github.sha }}
58
+ restore-keys: |
59
+ ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
60
+ ${{ runner.os }}-node-
61
+ prevention:
62
+ - "Never use github.sha as a primary cache key unless you explicitly want a per-commit cache that never restores"
63
+ - "Use hashFiles() over the relevant lock file or manifest as the stable portion of the cache key"
64
+ - "Always add restore-keys with progressively broader prefixes to benefit from partial cache hits"
65
+ - "Check the cache hit rate in the Actions UI — 0% hit rate on restore is a sign the key is too unique"
66
+ docs:
67
+ - url: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#using-the-cache-action
68
+ label: "GitHub Docs: Using the cache action"
69
+ - url: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#matching-a-cache-key
70
+ label: "GitHub Docs: Matching a cache key"
71
+ - url: https://stackoverflow.com/questions/59435153/github-actions-cache-miss-every-run
72
+ label: "Stack Overflow: GitHub Actions cache miss every run"
@@ -0,0 +1,114 @@
1
+ id: permissions-auth-045
2
+ title: "GITHUB_TOKEN has read-only access in pull_request workflows from forks — write operations return 403"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - github-token
7
+ - fork
8
+ - pull-request
9
+ - read-only
10
+ - permissions
11
+ - resource-not-accessible
12
+ - write-access
13
+ patterns:
14
+ - regex: 'Resource not accessible by integration'
15
+ flags: i
16
+ - regex: 'HttpError: Resource not accessible by integration'
17
+ flags: i
18
+ - regex: 'Error: HttpError: Not Found.*token.*forked'
19
+ flags: i
20
+ - regex: 'refused.*write.*403.*pull_request.*fork'
21
+ flags: i
22
+ error_messages:
23
+ - "Error: Resource not accessible by integration"
24
+ - "RequestError [HttpError]: Resource not accessible by integration"
25
+ - "Error: HttpError: Resource not accessible by integration"
26
+ root_cause: |
27
+ When a pull_request workflow is triggered by a PR from a forked repository, GitHub
28
+ automatically restricts the GITHUB_TOKEN to read-only permissions. This security measure
29
+ prevents malicious PRs from using the token to modify the base repository — the token
30
+ cannot create issues, post comments, create releases, trigger deployments, or write to
31
+ the repository. Workflows see a token that behaves as if all write permissions were denied.
32
+
33
+ This restriction applies even if the workflow YAML has explicit permissions: write entries.
34
+ The fork security boundary takes precedence. The workflow still runs (unlike pull_request_target
35
+ which runs with base repo permissions), but the token scope is silently reduced.
36
+
37
+ Common victims include: posting PR comments with github-script, uploading artifacts to
38
+ releases, creating check runs, and updating commit statuses.
39
+ fix: |
40
+ Use the pull_request_target event if the workflow must write to the base repo. Be aware
41
+ that pull_request_target checks out the base branch by default (not the PR head) and carries
42
+ security implications — never checkout and execute untrusted PR code with write-permission
43
+ tokens (see pwn request attacks). For safe comment posting, split into two workflows:
44
+ a read-only pull_request workflow that uploads results as an artifact, and a
45
+ pull_request_target or workflow_run workflow with write access that downloads the artifact
46
+ and posts the comment.
47
+ fix_code:
48
+ - language: yaml
49
+ label: "Split pattern — upload results in pull_request, post comment in workflow_run"
50
+ code: |
51
+ # Workflow 1: pr-test.yml — runs on pull_request (fork-safe, read-only token)
52
+ on: pull_request
53
+
54
+ jobs:
55
+ test:
56
+ runs-on: ubuntu-latest
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+ - name: Run tests and save results
60
+ run: npm test > test-results.txt 2>&1 || true
61
+ - name: Upload PR number for downstream workflow
62
+ uses: actions/upload-artifact@v4
63
+ with:
64
+ name: pr-results
65
+ path: |
66
+ test-results.txt
67
+ - language: yaml
68
+ label: "Workflow 2 — post comment using workflow_run (has write token)"
69
+ code: |
70
+ # Workflow 2: pr-comment.yml — runs after pr-test.yml completes (has write token)
71
+ on:
72
+ workflow_run:
73
+ workflows: ["PR Tests"]
74
+ types: [completed]
75
+
76
+ permissions:
77
+ pull-requests: write
78
+
79
+ jobs:
80
+ comment:
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - name: Download test results artifact
84
+ uses: actions/download-artifact@v4
85
+ with:
86
+ name: pr-results
87
+ run-id: ${{ github.event.workflow_run.id }}
88
+ github-token: ${{ secrets.GITHUB_TOKEN }}
89
+ - name: Post PR comment
90
+ uses: actions/github-script@v7
91
+ with:
92
+ script: |
93
+ const fs = require('fs');
94
+ const results = fs.readFileSync('test-results.txt', 'utf8');
95
+ await github.rest.issues.createComment({
96
+ owner: context.repo.owner,
97
+ repo: context.repo.repo,
98
+ issue_number: context.payload.workflow_run.pull_requests[0].number,
99
+ body: '## Test Results\n```\n' + results + '\n```'
100
+ });
101
+ prevention:
102
+ - "Know that GITHUB_TOKEN is read-only for PR workflows from forks — this is a security feature, not a misconfiguration"
103
+ - "Never use pull_request_target to run PR head code with write-permission tokens — this creates a pwn request vulnerability"
104
+ - "Use the upload-artifact + workflow_run split pattern for safe cross-fork write operations"
105
+ - "Test workflows with a fork PR specifically if they perform write operations — the token scope difference won't appear in same-repo PRs"
106
+ docs:
107
+ - url: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request
108
+ label: "GitHub Docs: pull_request event — fork permission restrictions"
109
+ - url: https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token
110
+ label: "GitHub Docs: GITHUB_TOKEN permissions"
111
+ - url: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
112
+ label: "GitHub Security Lab: Preventing pwn requests (pull_request_target risks)"
113
+ - url: https://stackoverflow.com/questions/70435286/resource-not-accessible-by-integration-on-github-pull-request-event
114
+ label: "Stack Overflow: Resource not accessible by integration on pull_request"
@@ -0,0 +1,87 @@
1
+ id: silent-failures-063
2
+ title: "actions/cache cache-hit output is empty string on miss, not 'false' — equality checks silently never match"
3
+ category: silent-failures
4
+ severity: silent-failure
5
+ tags:
6
+ - actions-cache
7
+ - cache-hit
8
+ - output
9
+ - empty-string
10
+ - if-condition
11
+ - false-comparison
12
+ patterns:
13
+ - regex: 'steps\.\w+\.outputs\.cache-hit\s*==\s*[''"]false[''"]'
14
+ flags: i
15
+ - regex: "cache-hit.*==.*'false'"
16
+ flags: i
17
+ error_messages:
18
+ - "cache-hit output comparison to 'false' always evaluates to false — use != 'true' instead"
19
+ root_cause: |
20
+ The actions/cache action sets the cache-hit output to the string 'true' when a cache is
21
+ restored from an exact key match, and to an empty string '' when the cache is not found
22
+ (including when restored from a restore-keys fallback, which returns '' for cache-hit in v3
23
+ and 'false' only in some v4 configurations). This means that
24
+ `if: steps.cache.outputs.cache-hit == 'false'` evaluates to false on a cache miss because
25
+ '' != 'false', so the step or job it guards is silently skipped.
26
+
27
+ This is one of the most upvoted GitHub Actions questions on Stack Overflow. Developers
28
+ naturally expect a boolean-like output to be 'true' or 'false', but the cache action uses
29
+ 'true' vs '' (empty) semantics. The step appears to run successfully; nothing in the logs
30
+ indicates the if condition did not match as expected.
31
+ fix: |
32
+ Check for cache miss using != 'true' instead of == 'false'. An empty string and 'false'
33
+ both evaluate to not-equal-to-'true', so this pattern correctly handles all cache miss
34
+ cases across cache action versions. Alternatively, use the boolean expression
35
+ `steps.cache.outputs.cache-hit != 'true'` or omit the check and rely on the cache action's
36
+ lookup-only: true parameter combined with a separate save step.
37
+ fix_code:
38
+ - language: yaml
39
+ label: "Use != 'true' to reliably detect cache miss (works across all cache action versions)"
40
+ code: |
41
+ - name: Cache dependencies
42
+ id: cache-deps
43
+ uses: actions/cache@v4
44
+ with:
45
+ path: ~/.npm
46
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
47
+
48
+ # WRONG — silently never runs on cache miss:
49
+ # - name: Install deps
50
+ # if: steps.cache-deps.outputs.cache-hit == 'false'
51
+
52
+ # CORRECT — matches both '' and 'false':
53
+ - name: Install dependencies
54
+ if: steps.cache-deps.outputs.cache-hit != 'true'
55
+ run: npm ci
56
+ - language: yaml
57
+ label: "Lookup-only + explicit save pattern (cache@v4) to avoid output ambiguity"
58
+ code: |
59
+ - name: Restore cache (lookup only, no auto-save)
60
+ id: cache-restore
61
+ uses: actions/cache/restore@v4
62
+ with:
63
+ path: ~/.npm
64
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
65
+
66
+ - name: Install dependencies
67
+ if: steps.cache-restore.outputs.cache-hit != 'true'
68
+ run: npm ci
69
+
70
+ - name: Save cache
71
+ if: steps.cache-restore.outputs.cache-hit != 'true'
72
+ uses: actions/cache/save@v4
73
+ with:
74
+ path: ~/.npm
75
+ key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
76
+ prevention:
77
+ - "Always use != 'true' to check for cache miss — never use == 'false' on cache action outputs"
78
+ - "Review the cache action version changelog: v3 uses '' on miss, v4 behavior depends on exact/partial hit"
79
+ - "Add a debug step that echoes cache-hit output to make cache behavior visible in logs during development"
80
+ - "Consult the cache action README for the exact semantics of cache-hit, cache-primary-key, and cache-matched-key"
81
+ docs:
82
+ - url: https://github.com/actions/cache#outputs
83
+ label: "actions/cache README: Outputs"
84
+ - url: https://stackoverflow.com/questions/62754195/github-actions-cache-hit-is-true-but-id-expect-false
85
+ label: "Stack Overflow: cache-hit is empty string, not 'false'"
86
+ - url: https://github.com/actions/cache/issues/1699
87
+ label: "actions/cache#1699: cache-hit output '' vs 'false' confusion"
@@ -0,0 +1,81 @@
1
+ id: triggers-045
2
+ title: "on.push.paths filter has no effect on workflow_dispatch — manual trigger always runs regardless of changed files"
3
+ category: triggers
4
+ severity: silent-failure
5
+ tags:
6
+ - workflow-dispatch
7
+ - push
8
+ - paths-filter
9
+ - triggers
10
+ - manual-trigger
11
+ - always-runs
12
+ patterns:
13
+ - regex: 'workflow_dispatch'
14
+ flags: i
15
+ error_messages:
16
+ - "Workflow ran unexpectedly when triggered via workflow_dispatch despite paths filter"
17
+ root_cause: |
18
+ GitHub Actions evaluates on.push.paths (and on.push.paths-ignore) filters only for push
19
+ events. The filter is tied to the event type it is defined under. When workflow_dispatch is
20
+ added as a separate trigger, it has no paths context — there is no commit diff associated
21
+ with a manual dispatch. GitHub therefore runs the workflow unconditionally when dispatched.
22
+
23
+ This surprises developers who add workflow_dispatch: to an existing push-triggered workflow
24
+ for convenience and expect the paths filter to still gate the run. The workflow_dispatch
25
+ event has no paths or branches key at all; any such keys would be silently ignored if added.
26
+
27
+ A related confusion: paths filters on pull_request or push do not protect against
28
+ workflow_dispatch, so adding a workflow_dispatch trigger effectively creates an unfiltered
29
+ entry point into the workflow.
30
+ fix: |
31
+ Accept that workflow_dispatch runs unconditionally — this is intentional GitHub behavior.
32
+ If you want to restrict what the dispatched workflow does based on inputs, use
33
+ workflow_dispatch inputs to pass a flag and gate steps with if: conditions. If the goal is
34
+ to limit accidental runs, remove workflow_dispatch from workflows where paths-gating is
35
+ critical, or add a required input that must be confirmed before proceeding.
36
+ fix_code:
37
+ - language: yaml
38
+ label: "Separate push-only workflow (with paths filter) from a dispatch-friendly workflow"
39
+ code: |
40
+ # Option 1: keep push and dispatch as separate workflow files
41
+ # push-filtered.yml — only triggered on relevant path changes
42
+ on:
43
+ push:
44
+ paths:
45
+ - 'src/**'
46
+
47
+ # dispatch-build.yml — no paths filter needed; always intentional
48
+ on:
49
+ workflow_dispatch:
50
+ - language: yaml
51
+ label: "Use a workflow_dispatch input to require confirmation before running"
52
+ code: |
53
+ on:
54
+ push:
55
+ paths:
56
+ - 'src/**'
57
+ workflow_dispatch:
58
+ inputs:
59
+ confirm:
60
+ description: 'Type YES to run regardless of changed files'
61
+ required: true
62
+
63
+ jobs:
64
+ build:
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - name: Proceed only if push triggered by path change OR dispatch confirmed
68
+ if: github.event_name == 'push' || inputs.confirm == 'YES'
69
+ run: echo "Running build"
70
+ prevention:
71
+ - "Understand that paths/branches filters are per-event-type — workflow_dispatch has no file diff context"
72
+ - "Document in the workflow file that workflow_dispatch runs unconditionally to avoid future confusion"
73
+ - "Use workflow_dispatch inputs to gate critical steps when a manual trigger is added to a path-filtered workflow"
74
+ - "Review which workflows have both push.paths filters and workflow_dispatch before adding the dispatch trigger"
75
+ docs:
76
+ - url: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch
77
+ label: "GitHub Docs: workflow_dispatch event"
78
+ - url: https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow#using-filters-to-target-specific-branches-or-tags-for-push-events
79
+ label: "GitHub Docs: Using filters to target specific paths"
80
+ - url: https://stackoverflow.com/questions/65900201/workflow-dispatch-ignores-paths-filter
81
+ label: "Stack Overflow: workflow_dispatch ignores paths filter"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@htekdev/actions-debugger",
3
- "version": "1.0.62",
3
+ "version": "1.0.63",
4
4
  "description": "65+ real GitHub Actions errors, queryable by agents. CLI + MCP server + Copilot skills + error database.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",