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
27 changes: 3 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
name: CI

on:
# No paths-ignore: Test + Build + Lint run on every PR so they can be
# required status checks without deadlocking docs-only / markdown-only PRs
# (a required check that never runs stays pending forever and blocks merge).
push:
branches: [master]
paths-ignore:
- 'docs/**'
- '**/*.md'
pull_request:
branches: [master]
paths-ignore:
- 'docs/**'
- '**/*.md'
workflow_dispatch:

concurrency:
Expand Down Expand Up @@ -72,21 +69,3 @@ jobs:
run: pnpm run build-with-lint
- name: Svelte check
run: pnpm run check

dependency-review:
name: Dependency Review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Dependency Review
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
comment-summary-in-pr: on-failure
fail-on-severity: high
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: [javascript-typescript]
language: [javascript-typescript, actions]
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Dependency Review

# Runs on every PR (no paths-ignore) so changes to docs/pnpm-lock.yaml are
# scanned too — the root CI workflow path-ignores docs/**, which previously
# left docs dependency bumps unreviewed.
on:
pull_request:
branches: [master]

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

permissions:
contents: read

jobs:
dependency-review:
name: Dependency Review
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Dependency Review
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
comment-summary-in-pr: on-failure
# Block newly-introduced moderate+ advisories in both runtime
# AND development scopes (the default only fails on runtime/high).
fail-on-severity: moderate
fail-on-scopes: runtime, development
18 changes: 15 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,25 @@ jobs:
with:
egress-policy: audit

# Mint a short-lived token for the "QuickAdd Release Bot" GitHub App.
# semantic-release pushes the release commit/tag with this token, so the
# push is attributed to the App, which is an allowed bypass actor on the
# master ruleset. The default GITHUB_TOKEN (github-actions[bot]) cannot
# be made a bypass actor, so its push would be blocked by branch protection.
- name: Generate release-bot token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
Comment on lines +47 to +50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/release.yml"
echo "== File =="
ls -la "$FILE"
echo
echo "== Lines 1-120 =="
nl -ba "$FILE" | sed -n '1,140p'
echo
echo "== Search for app-token outputs usage =="
rg -n "app-token|create-github-app-token|steps\.app-token\.outputs|app_token|github-app-token" "$FILE" || true
echo
echo "== Search for GITHUB_TOKEN permissions usage =="
rg -n "permissions:|GITHUB_TOKEN|token:" "$FILE" || true

Repository: chhoumann/quickadd

Length of output: 232


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/release.yml"
echo "== File =="
ls -la "$FILE"
echo
echo "== Lines 1-200 =="
nl -ba "$FILE" | sed -n '1,220p'
echo
echo "== Search for app-token outputs usage =="
rg -n "app-token|create-github-app-token|steps\.app-token\.outputs|app_token|github-app-token" "$FILE" || true
echo
echo "== Search for GITHUB_TOKEN and job permissions blocks =="
rg -n "permissions:|GITHUB_TOKEN|token:" "$FILE" || true

Repository: chhoumann/quickadd

Length of output: 232


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/release.yml"
echo "== File =="
ls -la "$FILE"
echo

echo "== Lines 1-220 =="
awk '{printf "%5d\t%s\n", NR, $0}' "$FILE" | sed -n '1,220p'
echo

echo "== Matching create-github-app-token usage (context) =="
rg -n "actions/create-github-app-token" "$FILE" -C 5 || true
echo

echo "== app-token step output usage (context) =="
rg -n "app-token|steps\.app-token|permission-|permission-contents|permission-issues|permission-pull-requests" "$FILE" -C 2 || true
echo

echo "== Any token: assignments used for checkout/semantic-release (context) =="
rg -n "token:\s*\${{\s*steps\.(app-token|app_token)|token:\s*\${{\s*secrets|checkout|semantic-release|actions/checkout" "$FILE" -C 3 || true
echo

echo "== Job permissions blocks (context) =="
rg -n "^\\s*permissions\\s*:" "$FILE" -C 3 || true

Repository: chhoumann/quickadd

Length of output: 12118


🌐 Web query:

actions/create-github-app-token v3.2.0 permission-contents permission-issues input names

💡 Result:

In the actions/create-github-app-token action, permissions are configured by prefixing the relevant GitHub API permission name with permission- (e.g., permission-contents or permission-issues) [1][2]. The inputs for permission-contents and permission-issues function as follows: - permission-contents: Sets the level of permission granted to the access token for repository contents, including commits, branches, downloads, releases, and merges [2]. It can be set to either read or write [3][2]. - permission-issues: Sets the level of permission granted to the access token for issues, including related comments, assignees, labels, and milestones [2]. It can be set to either read or write [3][2]. By default, if no permission-* inputs are provided, the generated token inherits all permissions currently granted to the GitHub App installation [1][4]. It is recommended to explicitly list only the permissions required for your specific use case to follow security best practices [1][2]. The action validates these inputs against the allowed permissions schema [5][2].

Citations:


🌐 Web query:

actions/create-github-app-token supported permission-* keys pull-requests releases

💡 Result:

The actions/create-github-app-token action supports all available GitHub App permission scopes by using inputs prefixed with permission- [1][2][3]. To specify a permission, you replace the underscores in the GitHub API permission name with dashes and prefix it with permission- (e.g., the pull_requests permission becomes permission-pull-requests and issues becomes permission-issues) [1][2]. Most permissions accept read or write as valid values, though specific permissions may have restricted options (e.g., workflows generally supports only write) [4][2][3]. Key points regarding these permissions include: - Default Behavior: If no permission-* inputs are provided, the generated token inherits all permissions currently granted to the GitHub App installation [1][2][3]. - Validation: Selected permissions must be granted to the GitHub App installation; requesting a permission the installation does not possess will result in an error [1][3]. - Mapping: The action internally maps the action input (e.g., permission-pull-requests) to the corresponding GitHub API scope (e.g., pull_requests) [2]. - Best Practice: It is recommended to explicitly list only the permissions required for your specific use case to follow the principle of least privilege [1]. For a complete list of all supported permission keys, you can refer to the action's official action.yml file, which defines these inputs dynamically [4][1][2].

Citations:


Constrain the GitHub App token permissions (least privilege)

actions/create-github-app-token mints an installation token with the GitHub App’s granted permissions; if you omit permission-* inputs, it inherits the App installation’s full scope, and the job-level permissions: block doesn’t reduce that scope. This App token is used for actions/checkout and for semantic-release (via GITHUB_TOKEN), so request only the scopes you need.

Suggested change
             - name: Generate release-bot token
               id: app-token
               uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
               with:
                   app-id: ${{ secrets.RELEASE_APP_ID }}
                   private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
+                  permission-contents: write
+                  permission-issues: write
+                  permission-pull-requests: write
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
permission-contents: write
permission-issues: write
permission-pull-requests: write
🧰 Tools
🪛 zizmor (1.25.2)

[error] 47-47: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions

(github-app)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 47 - 50, The GitHub App
installation token creation is granting the App's full installation scopes by
default; update the actions/create-github-app-token step to pass explicit
permission-* inputs limiting scopes to only what actions/checkout and
semantic-release require (for example add permission-contents: write,
permission-metadata: read, permission-actions: read — adjust to your repo’s
actual needs), so replace the current invocation with one that includes those
permission-* inputs to apply least-privilege.

Source: Linters/SAST tools


- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# semantic-release needs full history + tags and the persisted
# semantic-release needs full history + tags and the persisted App
# credential to push the release commit. Do not change these.
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ steps.app-token.outputs.token }}
Comment on lines +45 to +58

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the workflow file with line numbers (bounded to keep output small)
nl -ba .github/workflows/release.yml | sed -n '1,200p'

Repository: chhoumann/quickadd

Length of output: 105


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print workflow file with line numbers (no nl available)
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/release.yml | sed -n '1,220p'

Repository: chhoumann/quickadd

Length of output: 9464


Delay minting the bypass-capable GitHub App token until right before the Release step

Generate release-bot token mints the App token before pnpm install/build/test, and Checkout immediately persists it into git credentials via token: ${{ steps.app-token.outputs.token }}. This exposes a branch-protection-bypass credential to lifecycle/build/test code that can execute arbitrary scripts. Mint the token only after install/build/test pass, then set the git credential/remote and GITHUB_TOKEN for semantic-release immediately before the Release step.

🧰 Tools
🪛 zizmor (1.25.2)

[warning] 52-58: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 47-47: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions

(github-app)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 45 - 58, Move the "Generate
release-bot token" (step id: app-token) and the checkout that uses its token
(the actions/checkout with token: ${{ steps.app-token.outputs.token }}) so they
run only immediately before the Release step: remove the app-token minting and
the checkout-with-token from the pre-build stages (before pnpm
install/build/test), keep a normal checkout earlier without the app-token, then
add a new step sequence right before the Release job that (1) generates the
release-bot token via the existing create-github-app-token invocation (id:
app-token) and (2) performs a checkout or configures git to use ${{
steps.app-token.outputs.token }} and exports that token to GITHUB_TOKEN for the
semantic-release step; this ensures the bypass-capable App token is minted and
persisted into git credentials only after install/build/test succeed.

Source: Linters/SAST tools


- name: Setup pnpm
uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
Expand Down Expand Up @@ -74,7 +86,7 @@ jobs:
- name: Release
id: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
DRY_RUN: ${{ github.event.inputs.dryRun || 'false' }}
run: |
EXTRA_ARGS=""
Expand Down
4 changes: 3 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"pnpm": {
"overrides": {
"webpackbar": "^7.0.0",
"serialize-javascript": "^7.0.3"
"serialize-javascript": "^7.0.3",
"joi": "^17.13.4",
"uuid": "^11.1.1"
}
},
"browserslist": {
Expand Down
21 changes: 11 additions & 10 deletions docs/pnpm-lock.yaml

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

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/svelte": "^5.3.1",
"@testing-library/user-event": "^14.6.1",
"@types/node": "25.9.2",
"@types/node": "^25.9.3",
"@typescript-eslint/eslint-plugin": "^8.60.1",
"@typescript-eslint/parser": "^8.60.1",
"@vitest/coverage-v8": "4.1.8",
Expand All @@ -33,7 +33,7 @@
"eslint-plugin-svelte": "^3.19.0",
"globals": "17",
"jsdom": "29",
"obsidian": "1.13.0",
"obsidian": "1.13.1",
"obsidian-e2e": "0.6.0",
"semantic-release": "^25.0.5",
"svelte": "^5",
Expand All @@ -50,7 +50,7 @@
"chrono-node": "^2.9.1",
"fuse.js": "7",
"obsidian-dataview": "^0.5.68",
"svelte-dnd-action": "0.9.69",
"svelte-dnd-action": "0.9.70",
"three-way-merge": "^0.1.0",
"uuid": "14",
"zustand": "^5.0.14"
Expand All @@ -66,7 +66,7 @@
"esbuild"
],
"overrides": {
"obsidian-calendar-ui>svelte": "^3.59.2"
"obsidian-calendar-ui>svelte": "^5.56.3"
}
},
"release": {
Expand Down
Loading