Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
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
7 changes: 7 additions & 0 deletions .claude/settings/permissions/additional-dirs.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
// Additional directories that Claude Code can access
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"additionalDirectories": ["/tmp", "/workspaces"],
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 18, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope workspace access to the current repository path only.

Granting "/workspaces" provides access to all sibling workspaces in the container, which is broader than required for this skill flow and weakens least-privilege boundaries.

🤖 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 @.claude/settings/permissions/additional-dirs.jsonc at line 5, The
additionalDirectories entry currently exposes "/workspaces" too broadly; update
the "additionalDirectories" array to remove "/workspaces" and instead include
only the current repository path (referencing the "additionalDirectories" key
and its array values) — e.g., replace "/workspaces" with the repository-specific
mount (repo root like "." or an environment-derived repo path) so only the
current repo and "/tmp" remain accessible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Who cares, it's a devcontainer

Reply drafted by AI (Claude), reviewed and approved by the author before posting.

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.

@ejfine, fair point — the container boundary already provides the isolation, so least-privilege concerns about sibling paths under /workspaces don't apply in a devcontainer context. I'll note this for future reviews.

(ᵔᴥᵔ)🐇


✏️ Learnings added
Learnt from: ejfine
Repo: LabAutomationAndScreening/copier-nuxt-python-intranet-app PR: 163
File: .claude/settings/permissions/additional-dirs.jsonc:5-5
Timestamp: 2026-05-18T13:23:51.068Z
Learning: In devcontainer configurations (e.g., `.claude/settings/permissions/additional-dirs.jsonc`), granting Claude Code access to `/workspaces` is acceptable because the container itself provides the isolation boundary. Flagging this as a least-privilege issue is a false positive in devcontainer contexts.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

},
}
24 changes: 17 additions & 7 deletions .claude/settings/permissions/bash.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
"Bash(~/.claude/hooks/*)",
"Bash(~/.agents/skills/*)",
"Bash(~/.claude/skills/*)",
"Bash(.claude/skills/**/*)",
// File System
"Bash(bat *)",
"Bash(cd /workspaces/*)",
"Bash(chmod *)",
"Bash(cp *)",
"Bash(eza *)",
Expand All @@ -40,11 +42,15 @@
"Bash(touch *)",
"Bash(tree *)",
// Git & Version Control
"Bash(git add *)",
"Bash(git branch *)",
"Bash(git commit *)",
"Bash(git diff *)",
"Bash(git status *)",
"Bash(git log *)",
"Bash(git push)",
"Bash(git remote get-url origin)",
"Bash(git rev-parse *)",
"Bash(git branch *)",
"Bash(git status *)",
// Misc
"Bash(amp *)",
"Bash(atuin *)",
Expand All @@ -57,8 +63,10 @@
"Bash(test *)",
"Bash(zk *)",
// Node.js
"Bash(pnpm list *)",
"Bash(pnpm test-unit *)",
"Bash(pnpm test-e2e *)",
"Bash(pnpm view *)",
// Python
"Bash(uv run pytest *)",
// Text Processing
Expand All @@ -76,8 +84,9 @@
"Bash(rg *)",
// Research
"Bash(gh issue list *)",
"Bash(gh search *)",
"Bash(gh pr view *)",
"Bash(gh pr diff *)"
"Bash(gh pr diff *)",
],
"ask": [
// let's hold off before we let it use the github CLI in any free running allow mode...I don't want it somehow approving PRs with the user's credentials
Expand All @@ -89,19 +98,21 @@
"Bash(curl *)",
"Bash(ln *)",
"WebFetch",
// Git & Version Control
"Bash(git reset --hard *)",
],
"deny": [
// Exceptions to generally allowed AI tooling
"Bash(bd init*)", // we need to control the init process, don't let AI do that in the background
// Github
// Claude should not ever interfere with the PR process, that is how we gate AI's work
"Bash(gh pr close *)",
"Bash(gh pr comment *)",
"Bash(gh pr create *)",
"Bash(gh pr edit *)",
"Bash(gh pr merge *)",
"Bash(gh pr ready *)",
"Bash(gh pr review *)",
"Bash(gh pr merge *)",
"Bash(gh pr close *)",
"Bash(gh pr comment *)",
"Bash(gh pr update-branch *)",

// Destructive File Operations
Expand All @@ -118,7 +129,6 @@
"Bash(kill -9 *)",
"Bash(killall *)",
// Git & Version Control
"Bash(git reset --hard *)",
"Bash(git push -f *)",
"Bash(git push --force*)",
// Node.js
Expand Down
4 changes: 2 additions & 2 deletions .claude/settings/permissions/write.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
// This should only ever be running in a devcontainer, so pretty lenient permissions are allowed
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": ["Write(/tmp/**)"]
}
"allow": ["Write(/tmp/**/*)", "Write(/workspaces/**/tmp/**)", "Write(tmp/**)"],
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Narrow write scope to this repo’s tmp paths.

Write(/workspaces/**/tmp/**) enables cross-workspace writes. Restrict this to the current workspace to preserve least-privilege boundaries.

🤖 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 @.claude/settings/permissions/write.jsonc at line 6, The permission entry
"Write(/workspaces/**/tmp/**)" is too broad and allows cross-workspace writes;
narrow it to the current repository/workspace by replacing or removing that glob
and using a workspace-specific pattern (e.g., change
"Write(/workspaces/**/tmp/**)" to a pattern that targets only this workspace
such as "Write(/workspaces/<CURRENT_WORKSPACE>/**/tmp/**)" or remove it if the
other entries "Write(/tmp/**/*)" and "Write(tmp/**)" already cover needed
paths); update the entry in .claude/settings/permissions/write.jsonc where the
array contains "Write(/workspaces/**/tmp/**)" to enforce least-privilege.

},
}
290 changes: 290 additions & 0 deletions .claude/skills/address-pr-comments/SKILL.md

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions .claude/skills/address-pr-comments/check-footer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env python3
"""Ensure the AI attribution footer is present in a reply file.

Usage: check-footer.py <file>

Checks whether the footer is already in the file. If missing, appends it
with a blank line separator. Prints "present" or "added" to stdout.
"""

import sys
from pathlib import Path

EXPECTED_ARG_COUNT = 2

FOOTER = "*Reply drafted by AI (Claude), reviewed and approved by the author before posting.*"


def main() -> None:
if len(sys.argv) != EXPECTED_ARG_COUNT:
_ = sys.stderr.write(f"Usage: {sys.argv[0]} <file>\n")
sys.exit(1)

path = Path(sys.argv[1])
try:
content = path.read_text(encoding="utf-8")
except OSError as e:
_ = sys.stderr.write(f"File error for {path}: {e}\n")
sys.exit(1)

non_empty_lines = [line.strip() for line in content.splitlines() if line.strip()]
if non_empty_lines and non_empty_lines[-1] == FOOTER:
_ = sys.stdout.write("present\n")
return

# Ensure a blank line before the footer, then append.
suffix = "\n" if content.endswith("\n") else "\n\n"
try:
_ = path.write_text(content + suffix + FOOTER + "\n", encoding="utf-8")
except OSError as e:
_ = sys.stderr.write(f"File error for {path}: {e}\n")
sys.exit(1)
_ = sys.stdout.write("added\n")


if __name__ == "__main__":
main()
Loading