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
2 changes: 1 addition & 1 deletion .github/workflows/app-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
node-version: 22
runs-on: ${{ matrix.os }}
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
matrix:
language: [javascript-typescript]
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down Expand Up @@ -59,7 +59,7 @@ jobs:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
contents: write
id-token: write
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/renovate-approve.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
if: github.actor == 'renovate[bot]'
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/scheduled-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand All @@ -34,3 +34,8 @@ jobs:
run: pnpm lint:types

- run: pnpm test

- name: Scan dependencies with OSV-Scanner
uses: google/osv-scanner-action@v2
with:
fail-on-vuln: false
Comment on lines +38 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Workflow action references:"
rg -n '^\s*-\s+uses:\s+' .github/workflows

echo
echo "Non-SHA third-party action refs:"
rg -nP '^\s*-\s+uses:\s+(?!\./)(?!docker://)[^@]+@(?![0-9a-f]{40}\b)[^ #]+' .github/workflows

Repository: nazozokc/subtrack

Length of output: 4810


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "scheduled-ci.yml around the cited lines:"
sed -n '30,50p' .github/workflows/scheduled-ci.yml | cat -n

echo
echo "Full action refs in scheduled-ci.yml:"
rg -n '^\s*-\s+uses:\s+' .github/workflows/scheduled-ci.yml

Repository: nazozokc/subtrack

Length of output: 948


Pin google/osv-scanner-action to a full commit SHA. This is the only floating uses: entry in the workflow; a retagged @v2 can change scheduled CI behavior or execute different code at runtime.

🤖 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/scheduled-ci.yml around lines 38 - 41, The scheduled CI
workflow’s OSV-Scanner step is using a floating google/osv-scanner-action@v2
reference; update that uses entry in the workflow to a full commit SHA so the
action version is fixed and cannot change unexpectedly. Keep the step name and
fail-on-vuln setting as-is, and only replace the action reference in the Scan
dependencies with OSV-Scanner block.

2 changes: 1 addition & 1 deletion .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
security-events: write

steps:
- uses: step-security/harden-runner@v2
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

Expand Down
14 changes: 8 additions & 6 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,19 @@ The following are **out of scope**:

subtrack takes supply chain security seriously:

- **Provenance attestation**: All npm releases use `pnpm publish --provenance` (SLSA Level 1+). Release binaries also have build provenance attestation via `actions/attest-build-provenance`.
- **Provenance attestation**: All npm releases use `pnpm publish --provenance` (SLSA Level 1+) with `publishConfig.provenance: true` in package.json. Release binaries also have build provenance attestation via `actions/attest-build-provenance`.
- **SBOM generation**: Every release generates an SPDX Software Bill of Materials, attached to the GitHub release.
- **Dependency review**: Every pull request is scanned for new vulnerabilities via GitHub's dependency review action.
- **Renovate bot**: Dependencies are updated with a 7-day minimum release age to detect malicious releases before they reach this project. Renovate PRs are only auto-approved when auto-merge is enabled (skipping major/dashboard-approval updates).
- **Dependabot**: Defense-in-depth alongside Renovate — catches vulnerabilities via GitHub Advisory Database integration.
- **OSV-Scanner**: Scheduled CI runs Google's OSV-Scanner against all dependencies to detect known vulnerabilities across open source databases.
- **Renovate bot**: Dependencies are updated with a 7-day minimum release age to detect malicious releases before they reach this project. Renovate PRs are only auto-approved when auto-merge is enabled (skipping major/dashboard-approval updates). OSV vulnerability alerts are enabled in the dependency dashboard.
- **Dependabot**: Defense-in-depth alongside Renovate — catches vulnerabilities faster via GitHub Advisory Database integration.
- **Lockfile**: A `pnpm-lock.yaml` is committed and verified with `--frozen-lockfile` in CI. An `npm-shrinkwrap.json` is generated at publish time for downstream reproducability.
- **Limited build scripts**: Only `esbuild` is permitted to run install scripts (`allowBuilds` in `pnpm-workspace.yaml`).
- **Harden-Runner**: Every CI workflow uses `step-security/harden-runner` for runtime egress monitoring.
- **Harden-Runner**: Every CI workflow uses `step-security/harden-runner` (pinned to v2.19.4 SHA) for runtime egress monitoring and threat detection.
- **CodeQL**: Static analysis runs on every push and PR.
- **pnpm audit**: Runs in CI to catch known vulnerabilities.
- **OpenSSF Scorecard**: Automated supply chain health assessment.
- **Tag signing**: Release tags should be signed with GPG/SSH — see [Sigining tags](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-tags).
- **OpenSSF Scorecard**: Automated supply chain health assessment with results published to the repository's Security tab.
- **Tag signing**: Release tags should be signed with GPG/SSH — see [Signing tags](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-tags).
- **Trojan Source detection**: CI scans for Unicode bidirectional control characters in all source files.
- **secretlint**: CI scans for accidentally committed secrets and credentials.
- **Socket.dev**: Published package monitored on [Socket.dev](https://socket.dev/npm/package/subtrack) for supply chain risk indicators.
50 changes: 50 additions & 0 deletions pnpm-lock.yaml

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

7 changes: 5 additions & 2 deletions subtrack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "subtrack",
"version": "7.0.8",
"version": "8.0.0",
"author": "nazozokc",
"type": "module",
"license": "MIT",
Expand All @@ -9,7 +9,8 @@
"subtrack": "./dist/index.mjs"
},
"publishConfig": {
"access": "public"
"access": "public",
"provenance": true
},
"description": "CLI tool to manage subscription services from the terminal",
"keywords": [
Expand Down Expand Up @@ -65,13 +66,15 @@
"ink": "^7.1.0",
"ink-gradient": "^4.0.1",
"ink-spinner": "^5.0.0",
"node-notifier": "^10.0.1",
"picocolors": "^1.1.1",
"react": "^19.2.7",
"sql.js": "^1.14.1"
},
"devDependencies": {
"@secretlint/secretlint-rule-preset-recommend": "^13.0.2",
"@types/node": "^26.0.0",
"@types/node-notifier": "^8.0.5",
Comment on lines +69 to +77

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does @types/node-notifier 8.x cover node-notifier 10 API, or does node-notifier ship its own types?

💡 Result:

Node-notifier does not ship its own type definitions; it relies on the community-maintained @types/node-notifier package on DefinitelyTyped [1][2][3]. Regarding compatibility, @types/node-notifier 8.x was initially designed for older versions of the library. However, as of March 2026, the type definitions have been updated to align with the node-notifier v10 API surface (see pull request #74675 on DefinitelyTyped) [4]. If you are using node-notifier 10, ensure you are using the latest version of @types/node-notifier to access the correct API definitions, including additions such as the root Notification constructor alias, EventEmitter inheritance, and updated options for WindowsToaster and NotificationCenter [4].

Citations:


🌐 Web query:

npm registry @types/node-notifier latest version and compatibility with node-notifier v10

💡 Result:

The latest version of the @types/node-notifier package on the npm registry is 8.0.5 [1]. Regarding compatibility with node-notifier v10: while the package version 8.0.5 has served as the standard type definition, it does not fully align with the API surface of node-notifier v10.0.1 [2]. As of early 2026, a pull request (DefinitelyTyped PR #74675) has been developed to specifically align these type definitions with the node-notifier v10 API, including additions such as the root Notification constructor alias, EventEmitter inheritance for notifier classes, and updated options for WindowsToaster and NotifySend [2]. If you are using node-notifier v10, you may encounter missing type definitions if you rely solely on version 8.0.5 [2]. Users requiring comprehensive support for v10 features should check the status of the aforementioned DefinitelyTyped updates or consider extending the existing types locally [2].

Citations:


Keep @types/node-notifier aligned with node-notifier v10. @types/node-notifier@8.0.5 still lags the v10 API surface, so this pairing can miss newer declarations. Update the typings once the v10-aligned release is available, or add the missing declarations locally.

🤖 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 `@subtrack/package.json` around lines 68 - 76, The dependency setup in
package.json has a version mismatch between node-notifier v10 and
`@types/node-notifier` v8, so align the typings with the v10 API surface. Update
the `@types/node-notifier` entry in the devDependencies section to the matching
v10-compatible release when available, or add/adjust local declarations so the
types used by node-notifier stay in sync.

"@types/react": "^19.2.17",
"@types/sql.js": "^1.4.11",
"husky": "^9.1.7",
Expand Down
11 changes: 11 additions & 0 deletions subtrack/src/__tests__/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ beforeAll(async () => {
notes TEXT,
created_at TEXT NOT NULL DEFAULT (date('now'))
)`)
testDb.run(`CREATE TABLE IF NOT EXISTS price_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
subscription_id INTEGER NOT NULL,
old_price INTEGER,
new_price INTEGER NOT NULL,
old_currency TEXT,
new_currency TEXT NOT NULL,
changed_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE CASCADE
)`)

const db = await import("../db.ts")
db.__setDb(testDb)
Expand All @@ -149,6 +159,7 @@ beforeAll(async () => {
})

beforeEach(() => {
testDb.run("DELETE FROM price_history")
testDb.run("DELETE FROM subscription_tags")
testDb.run("DELETE FROM tags")
testDb.run("DELETE FROM subscriptions")
Expand Down
Loading
Loading