Skip to content

Commit cabac0d

Browse files
committed
remove typeGPT, added groq and allow user to use any model by adding API keys
1 parent 210e03f commit cabac0d

38 files changed

Lines changed: 1400 additions & 691 deletions
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
name: package-security-check
3+
description: Check whether a package is secure before installing or adding it as a dependency. Use this skill EVERY time you are about to install a package or add a dependency — e.g. npm install, npm i, yarn add, pnpm add, pip install, adding entries to package.json or requirements.txt — or whenever the user asks whether a package is safe, secure, trustworthy, vulnerable, or asks for a security check/score of a package. Trigger even if the user doesn't mention security, because installing any third-party package should be preceded by this check.
4+
---
5+
6+
# Package Security Check
7+
8+
Before installing any third-party package, verify it has no known vulnerabilities and a reasonable security health score. If it's clean, proceed with the installation. If not, STOP and show the user a warning, then let them decide.
9+
10+
## Workflow
11+
12+
1. **Run the check** before executing any install command:
13+
14+
```bash
15+
python3 scripts/check_package.py <package-name> [version] [--ecosystem npm]
16+
```
17+
18+
- If the user specified a version (e.g. `lodash@4.17.20`), pass it. Otherwise the script checks the latest version.
19+
- Ecosystem defaults to `npm`. Also supports `PyPI`, `Go`, `Maven`, `NuGet`, `RubyGems`, `crates.io`.
20+
- When installing multiple packages, check each one.
21+
22+
2. **Act on the exit code:**
23+
24+
- **Exit 0 (SECURE)** — proceed with the installation without asking. Briefly mention the check passed, e.g. "✓ express@4.19.2 — no known vulnerabilities, Scorecard 7.6/10. Installing…"
25+
- **Exit 1 (WARNING)** — do NOT install yet. Show the user a clear warning and ask whether to proceed, pick a different version, or choose an alternative package. See the warning format below.
26+
- **Exit 2 (ERROR)** — the check itself failed (network, package not found). Tell the user the check could not be completed and ask whether to proceed unverified. Never silently install after a failed check.
27+
28+
3. **If the user explicitly says to skip the check or install anyway**, respect that — this skill informs, it doesn't block.
29+
30+
## What the script checks
31+
32+
- **Known vulnerabilities** via OSV.dev — authoritative CVE/GHSA data for the exact version. Any hit triggers a warning.
33+
- **OpenSSF Scorecard** via deps.dev — a 0–10 health heuristic (code review, maintenance, branch protection, etc.). Scores below 4.0 trigger a warning. This is a signal, not proof of insecurity — say so when warning about score alone.
34+
- A missing Scorecard (no public GitHub repo, or repo not scanned) is common and NOT by itself a reason to warn.
35+
36+
## Warning format
37+
38+
When the verdict is WARNING, present it like this before asking how to proceed:
39+
40+
```
41+
⚠️ Security warning: <package>@<version>
42+
43+
Known vulnerabilities:
44+
- GHSA-xxxx [HIGH] Prototype pollution in ...
45+
(fixed in <version>, if the script output mentions one)
46+
47+
OpenSSF Scorecard: 2.9/10 (low — weak maintenance/review signals)
48+
49+
Options:
50+
1. Install a patched version (recommended if one exists)
51+
2. Pick an alternative package
52+
3. Install anyway
53+
```
54+
55+
When a vulnerability is fixed in a newer version, recommend installing that version as the default suggestion.
56+
57+
## Notes & edge cases
58+
59+
- Scoped npm packages work as-is (`@nestjs/core`) — the script handles URL-encoding.
60+
- For version *ranges* in package.json, check the version that would actually resolve (run `npm view <pkg> version` to find it if unsure).
61+
- This check covers known vulnerabilities and project health. It does NOT detect zero-day malware or typosquatting — if a package name looks like a misspelling of a popular package (e.g. `expresss`, `lodahs`), flag that to the user separately.
62+
- The APIs used (api.osv.dev, api.deps.dev) are free and require no authentication. If the environment blocks these domains, report exit code 2 behavior.
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#!/usr/bin/env python3
2+
"""
3+
check_package.py - Check whether a package is safe to install.
4+
5+
Data sources (all free, no API key):
6+
1. OSV.dev -> known vulnerabilities (ground truth: CVEs/GHSAs)
7+
2. deps.dev -> package metadata + OpenSSF Scorecard (health score 0-10)
8+
9+
Usage:
10+
python3 check_package.py <package-name> [version] [--ecosystem npm]
11+
12+
Examples:
13+
python3 check_package.py lodash 4.17.20
14+
python3 check_package.py express
15+
python3 check_package.py requests 2.19.0 --ecosystem PyPI
16+
17+
Exit codes:
18+
0 = SECURE (no known vulnerabilities, acceptable health score)
19+
1 = WARNING (known vulnerabilities and/or low health score)
20+
2 = ERROR (network failure, package not found, etc.)
21+
"""
22+
23+
import argparse
24+
import json
25+
import sys
26+
import urllib.parse
27+
import urllib.request
28+
29+
OSV_API = "https://api.osv.dev/v1/query"
30+
DEPSDEV_API = "https://api.deps.dev/v3"
31+
SCORECARD_WARN_THRESHOLD = 4.0 # warn if OpenSSF score is below this
32+
TIMEOUT = 15
33+
34+
35+
def http_json(url, payload=None):
36+
req = urllib.request.Request(
37+
url,
38+
data=json.dumps(payload).encode() if payload is not None else None,
39+
headers={"Content-Type": "application/json",
40+
"User-Agent": "package-security-check"},
41+
method="POST" if payload is not None else "GET",
42+
)
43+
with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
44+
return json.loads(resp.read().decode())
45+
46+
47+
def get_latest_version(name, ecosystem):
48+
"""Resolve the default/latest version from deps.dev."""
49+
url = f"{DEPSDEV_API}/systems/{ecosystem}/packages/{urllib.parse.quote(name, safe='')}"
50+
data = http_json(url)
51+
for v in data.get("versions", []):
52+
if v.get("isDefault"):
53+
return v["versionKey"]["version"]
54+
versions = data.get("versions", [])
55+
return versions[-1]["versionKey"]["version"] if versions else None
56+
57+
58+
def query_osv(name, version, ecosystem):
59+
"""Return list of known vulnerabilities for this package version."""
60+
payload = {"package": {"name": name, "ecosystem": ecosystem}}
61+
if version:
62+
payload["version"] = version
63+
data = http_json(OSV_API, payload)
64+
return data.get("vulns", [])
65+
66+
67+
def query_scorecard(name, version, ecosystem):
68+
"""Return (score, repo) from deps.dev's OpenSSF Scorecard data, or (None, None)."""
69+
url = (
70+
f"{DEPSDEV_API}/systems/{ecosystem}/packages/"
71+
f"{urllib.parse.quote(name, safe='')}/versions/{urllib.parse.quote(version, safe='')}"
72+
)
73+
data = http_json(url)
74+
for project in data.get("relatedProjects", []):
75+
project_key = project.get("projectKey", {}).get("id")
76+
if not project_key:
77+
continue
78+
try:
79+
pdata = http_json(
80+
f"{DEPSDEV_API}/projects/{urllib.parse.quote(project_key, safe='')}")
81+
except Exception:
82+
continue
83+
scorecard = pdata.get("scorecard")
84+
if scorecard and "overallScore" in scorecard:
85+
return scorecard["overallScore"], project_key
86+
return None, None
87+
88+
89+
def severity_label(vuln):
90+
"""Best-effort severity extraction from an OSV record."""
91+
sev = vuln.get("database_specific", {}).get("severity")
92+
if sev:
93+
return sev.upper()
94+
for s in vuln.get("severity", []):
95+
if s.get("type", "").startswith("CVSS"):
96+
return f"CVSS {s.get('score', '?')}"
97+
return "UNKNOWN"
98+
99+
100+
def main():
101+
parser = argparse.ArgumentParser()
102+
parser.add_argument("package")
103+
parser.add_argument("version", nargs="?", default=None)
104+
parser.add_argument("--ecosystem", default="npm",
105+
help="npm (default), PyPI, Go, Maven, NuGet, RubyGems, crates.io")
106+
parser.add_argument("--json", action="store_true",
107+
help="machine-readable output")
108+
args = parser.parse_args()
109+
110+
name, version, eco = args.package, args.version, args.ecosystem
111+
result = {"package": name, "ecosystem": eco, "version": version,
112+
"vulnerabilities": [], "scorecard": None, "verdict": None, "notes": []}
113+
114+
# Resolve version if not given (needed for precise OSV + scorecard lookup)
115+
if not version:
116+
try:
117+
version = get_latest_version(name, eco)
118+
result["version"] = version
119+
result["notes"].append(
120+
f"No version specified; checked latest ({version}).")
121+
except Exception as e:
122+
result["notes"].append(f"Could not resolve latest version: {e}")
123+
124+
# 1. Known vulnerabilities (OSV) — this is the authoritative check
125+
try:
126+
vulns = query_osv(name, version, eco)
127+
for v in vulns:
128+
result["vulnerabilities"].append({
129+
"id": v.get("id"),
130+
"summary": (v.get("summary") or v.get("details", ""))[:140],
131+
"severity": severity_label(v),
132+
})
133+
except Exception as e:
134+
result["notes"].append(f"OSV query failed: {e}")
135+
result["verdict"] = "ERROR"
136+
137+
# 2. Health score (OpenSSF Scorecard via deps.dev) — heuristic signal
138+
if version:
139+
try:
140+
score, repo = query_scorecard(name, version, eco)
141+
if score is not None:
142+
result["scorecard"] = {"score": score, "repo": repo}
143+
else:
144+
result["notes"].append(
145+
"No OpenSSF Scorecard available (no scored GitHub repo).")
146+
except Exception as e:
147+
result["notes"].append(f"Scorecard lookup failed: {e}")
148+
149+
# Verdict
150+
if result["verdict"] != "ERROR":
151+
vuln_count = len(result["vulnerabilities"])
152+
low_score = (result["scorecard"] is not None
153+
and result["scorecard"]["score"] < SCORECARD_WARN_THRESHOLD)
154+
result["verdict"] = "WARNING" if (
155+
vuln_count or low_score) else "SECURE"
156+
157+
# Output
158+
if args.json:
159+
print(json.dumps(result, indent=2))
160+
else:
161+
v = result["verdict"]
162+
icon = {"SECURE": "[OK]", "WARNING": "[!!]", "ERROR": "[??]"}[v]
163+
print(f"{icon} {v}: {name}@{result['version'] or '?'} ({eco})")
164+
if result["vulnerabilities"]:
165+
print(
166+
f"\nKnown vulnerabilities ({len(result['vulnerabilities'])}):")
167+
for vu in result["vulnerabilities"]:
168+
print(f" - {vu['id']} [{vu['severity']}] {vu['summary']}")
169+
if result["scorecard"]:
170+
sc = result["scorecard"]
171+
flag = " (below threshold!)" if sc["score"] < SCORECARD_WARN_THRESHOLD else ""
172+
print(
173+
f"\nOpenSSF Scorecard: {sc['score']}/10{flag} repo: {sc['repo']}")
174+
for note in result["notes"]:
175+
print(f"note: {note}")
176+
177+
sys.exit({"SECURE": 0, "WARNING": 1, "ERROR": 2}[result["verdict"]])
178+
179+
180+
if __name__ == "__main__":
181+
main()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
name: react-doctor
3+
description: Use when finishing a feature, fixing a bug, before committing React code, or when the user types `/doctor`, asks to scan, triage, or clean up React diagnostics. Covers lint, accessibility, bundle size, architecture. Includes a regression check and a full local-triage workflow that fetches the canonical playbook.
4+
version: "1.2.0"
5+
---
6+
7+
# React Doctor
8+
9+
Scans React codebases for security, performance, correctness, and architecture issues. Outputs a 0–100 health score.
10+
11+
## After making React code changes:
12+
13+
Run `npx react-doctor@latest --verbose --diff` and check the score did not regress.
14+
15+
If the score dropped, fix the regressions before committing.
16+
17+
## For general cleanup or code improvement:
18+
19+
Run `npx react-doctor@latest --verbose` (without `--diff`) to scan the full codebase. Fix issues by severity — errors first, then warnings.
20+
21+
## /doctor — full local triage workflow
22+
23+
When the user types `/doctor`, says "run react doctor", or asks for a full triage / cleanup pass (not just a regression check), fetch the canonical local-triage playbook and follow every step in it:
24+
25+
```bash
26+
curl --fail --silent --show-error \
27+
--header 'Cache-Control: no-cache' \
28+
https://www.react.doctor/prompts/react-doctor-agent.md
29+
```
30+
31+
The playbook is the single source of truth — a scan → filter → triage → fix → validate loop that edits the working tree directly (never commits, never opens PRs). Updating the prompt at its source updates every agent on its next fetch — no skill reinstall needed.
32+
33+
Pair it with the matching per-rule prompts at `https://www.react.doctor/prompts/rules/<plugin>/<rule>.md` (fetched on demand inside the playbook) so each fix uses the canonical, reviewer-tested recipe.
34+
35+
## Configuring or explaining rules
36+
37+
When the user wants to understand a rule, disagrees with one, or wants to disable / tune which rules run (not fix code), read [references/explain.md](references/explain.md) and follow it. Start with `npx react-doctor@latest rules explain <rule>`, then apply the narrowest control via `npx react-doctor@latest rules disable|set|category|ignore-tag …`, which edits your `doctor.config.*` (or `package.json#reactDoctor`).
38+
39+
## Command
40+
41+
```bash
42+
npx react-doctor@latest --verbose --diff
43+
```
44+
45+
| Flag | Purpose |
46+
| ----------- | --------------------------------------------- |
47+
| `.` | Scan current directory |
48+
| `--verbose` | Show affected files and line numbers per rule |
49+
| `--diff` | Only scan changed files vs base branch |
50+
| `--score` | Output only the numeric score |
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Explaining and configuring rules
2+
3+
Explain React Doctor rules and edit `doctor.config.*` safely. Use this when a user
4+
wants to understand a rule or change which rules run — not for fixing diagnostics
5+
(that is the main `react-doctor` skill / `/doctor`).
6+
7+
Triggers: "why did this rule fire", "I disagree with this rule", "turn this rule off",
8+
"stop flagging X", "too noisy", "disable design rules".
9+
10+
## Workflow
11+
12+
1. Identify the rule key from the diagnostic (e.g. `react-doctor/no-array-index-as-key`).
13+
2. Explain it before changing anything:
14+
15+
```bash
16+
npx react-doctor@latest rules explain react-doctor/no-array-index-as-key
17+
```
18+
19+
3. Pick the narrowest control that matches the user's intent (see decision guide).
20+
4. Apply it with a `rules` subcommand (edits your `doctor.config.*` or `package.json#reactDoctor` in place, preserving other fields and formatting).
21+
5. Validate the change did what they wanted:
22+
23+
```bash
24+
npx react-doctor@latest --verbose --diff
25+
```
26+
27+
## Commands
28+
29+
```bash
30+
npx react-doctor@latest rules list # every rule + its effective severity
31+
npx react-doctor@latest rules list --configured # only what your config changed
32+
npx react-doctor@latest rules list --category Performance # filter by category
33+
npx react-doctor@latest rules explain <rule> # why it matters + how to configure
34+
npx react-doctor@latest rules disable <rule> # rule never runs
35+
npx react-doctor@latest rules enable <rule> # turn back on at its recommended severity
36+
npx react-doctor@latest rules set <rule> warn # off | warn | error
37+
npx react-doctor@latest rules category "React Native" off # whole category
38+
npx react-doctor@latest rules ignore-tag design # skip a rule family (design, test-noise, …)
39+
npx react-doctor@latest rules unignore-tag design
40+
```
41+
42+
Rule references accept the full key (`react-doctor/no-danger`), the bare id (`no-danger`), or a legacy key (`react/no-danger`).
43+
44+
## Decision guide
45+
46+
Match the control to the intent — prefer the narrowest one:
47+
48+
- **User disagrees with one rule / it's a false positive for them**`rules disable <rule>` (sets `rules.<key> = "off"`; the rule stops running everywhere). This is the default for "I don't want this rule".
49+
- **Rule is fine but wrong severity**`rules set <rule> warn` or `rules set <rule> error`.
50+
- **A disabled-by-default rule they want on**`rules enable <rule>`.
51+
- **A whole area is unwanted** (e.g. all React Native rules) → `rules category "<Category>" off`.
52+
- **A behavioral family is noisy** (`design`, `test-noise`, `migration-hint`) → `rules ignore-tag <tag>`.
53+
- **Keep it locally but hide from PR comment / score / CI gate only** → do NOT disable. Edit `surfaces` in your config (`surfaces.prComment.excludeRules`, `surfaces.score.excludeTags`, `surfaces.ciFailure.excludeCategories`). The rule still shows in local `cli` output.
54+
55+
How the layers combine: `ignore.tags` disables every rule carrying that tag **before** linting, so a tagged rule stays off even if `rules`/`categories` set it to `warn`/`error` (a rule-level override cannot re-enable a tag-ignored rule). For rules that aren't tag-disabled, `rules` overrides `categories` overrides the rule's default. `surfaces` is visibility-only and never changes whether a rule runs.
56+
57+
## Config shape
58+
59+
Config lives in `doctor.config.ts` (or `.js`/`.mjs`/`.cjs`/`.json`/`.jsonc`), or the `reactDoctor` key in `package.json`. The `rules` commands edit whichever exists — TS/JS edits preserve formatting (via magicast) — and create `doctor.config.json` when none does, stamping `$schema`:
60+
61+
```ts
62+
// doctor.config.ts
63+
export default {
64+
rules: { "react-doctor/no-array-index-as-key": "off" },
65+
categories: { "React Native": "warn" },
66+
ignore: { tags: ["design"] },
67+
};
68+
```
69+
70+
## Educating the user
71+
72+
When explaining a rule, lead with the "Why it matters" guidance from `rules explain` and, when they want depth, the per-rule recipe at `https://www.react.doctor/prompts/rules/<plugin>/<rule>.md`. Only after they understand it should you offer to disable it — many "bad" rules are catching real issues.

.github/workflows/react-doctor.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: React Doctor
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
push:
7+
branches: [master]
8+
9+
permissions:
10+
contents: read
11+
pull-requests: write
12+
issues: write
13+
statuses: write
14+
15+
concurrency:
16+
group: react-doctor-${{ github.event.pull_request.number || github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
react-doctor:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v5
24+
with:
25+
fetch-depth: 0
26+
- uses: millionco/react-doctor@v2

0 commit comments

Comments
 (0)