fix(security): ignore .env so a generated portal skill's API token can't be committed (#303)

/add-portal can generate a skill for a portal that only returns usable
content through a paid fetching service, and such a skill reads its API
token from the environment. Nothing stopped the `.env` holding that token
from being committed: `.gitignore` had no `.env` rule, and
`REQUIRED_IGNORE_RULES` in tools/security_guards.py did not pin one.

No shipped portal needs a credential - all six are free and
unauthenticated - so upstream has never hit this. A fork whose generated
portals do need one hits it on the first `git add -A`.

Add `.env` and `.env.*` to `.gitignore`, and pin both in
`REQUIRED_IGNORE_RULES` so the guard fails if the rule is later dropped.
No negation rule is added, so `ALLOWED_IGNORE_NEGATIONS` is untouched.

Verified:
  - `printf 'X=y' > .env && git check-ignore -v .env` -> matched
  - dropping the `.env` line makes `python3 tools/security_guards.py`
    report the missing rule and fail; restoring it returns OK
  - `lint_skills`, `check_framework_version`, `security_guards` all OK;
    `python3 -m unittest discover -s tests` 196 passed

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Muhammad Haseeb
2026-08-07 15:49:40 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 85b3ddc243
commit a7ac6fea75
3 changed files with 18 additions and 0 deletions
+5
View File
@@ -29,6 +29,11 @@ salary_data.json
*_BehavioralReport.pdf *_BehavioralReport.pdf
linkedin_Profile.pdf linkedin_Profile.pdf
# Secrets. A portal skill generated by /add-portal may need an API token for a
# fetching service; the .env holding it must never be committed.
.env
.env.*
# Personal photos and signatures # Personal photos and signatures
*.jpg *.jpg
*.jpeg *.jpeg
+8
View File
@@ -45,6 +45,14 @@ per-file diff commands.
### Fixed ### Fixed
- **A `.env` was committable** (`.gitignore`, `tools/security_guards.py`). `/add-portal`
can generate a skill for a portal that only returns usable content through a paid
fetching service, and such a skill reads an API token from the environment - but
nothing stopped the `.env` holding that token from being committed. No shipped portal
needs a credential, so upstream never hit this; a fork whose generated portals do hit
it immediately. `.env` and `.env.*` are now ignored and pinned in
`REQUIRED_IGNORE_RULES`, so the guard fails if the rule is ever dropped.
- **The robots gate did not fail closed** (`tools/robots_check.py`, #277). Found by an - **The robots gate did not fail closed** (`tools/robots_check.py`, #277). Found by an
adversarial review run over the merged file, not by inspection. Both cases are pinned adversarial review run over the merged file, not by inspection. Both cases are pinned
in `tests/test_robots_check.py` as FAIL-OPEN REGRESSIONs: in `tests/test_robots_check.py` as FAIL-OPEN REGRESSIONs:
+5
View File
@@ -70,6 +70,11 @@ REQUIRED_IGNORE_RULES = [
"gmail_sync/", "gmail_sync/",
"reports/", "reports/",
"upskill/*.md", "upskill/*.md",
# Not personal data but the same failure mode: /add-portal can generate a
# skill for a portal that only returns usable content through a paid
# fetching service, and that skill reads an API token from the environment.
".env",
".env.*",
] ]
# Negation (re-include) rules the template legitimately ships. .gitignore is # Negation (re-include) rules the template legitimately ships. .gitignore is