-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscreen.sh
More file actions
executable file
·222 lines (177 loc) · 6.76 KB
/
screen.sh
File metadata and controls
executable file
·222 lines (177 loc) · 6.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/usr/bin/env bash
set -euo pipefail
VERSION="5.0.1"
DISPLAY_NAME="screener"
MACHINE_TYPE="basicLinux32gb"
MARKER=".screening-tools-installed"
REPO_NAME="screening-reports"
# --- Helpers ---
die() { echo "ERROR: $1" >&2; exit 1; }
usage() {
cat <<'USAGE'
screen.sh — One-command sandboxed GitHub repo screening
Usage:
screen-repo <repo-url> [options]
screen-repo --help
Options:
--destroy Delete Codespace after screening (full wipe)
--fresh Force new Codespace (ignore existing)
--help Show this help
Examples:
screen-repo https://github.com/owner/repo
screen-repo https://github.com/owner/repo --destroy
screen-repo https://github.com/owner/repo --fresh
First Run:
1. Creates a Codespace, installs Claude Code CLI
2. Opens interactive SSH — you run: claude login
3. Then run: claude "screen <url>"
4. Exit when done. Codespace stops (persists for reuse).
Subsequent Runs (Claude already logged in):
1. Reuses existing Codespace, opens interactive SSH
2. Run: claude "screen <url>"
3. Exit when done. Codespace stops.
Security Tradeoffs:
REUSE (default):
- Codespace stops after screening, restarts on next run
- Tools + Claude login persist — fast subsequent screenings
- Risk: a malicious repo could leave artifacts that affect next screening
- Best for: routine screenings of repos you expect are probably safe
DESTROY (--destroy flag):
- Codespace deleted after screening — nothing persists
- Next run: full reinstall + Claude re-login required
- Risk: none — completely fresh environment every time
- Best for: high-risk targets you suspect are genuinely malicious
USAGE
exit 0
}
# --- Parse Args ---
TARGET_URL=""
DESTROY=false
FRESH=false
for arg in "$@"; do
case "$arg" in
--destroy) DESTROY=true ;;
--fresh) FRESH=true ;;
--help|-h) usage ;;
https://github.com/*)
TARGET_URL="$arg" ;;
*)
die "Unknown argument: $arg" ;;
esac
done
[[ -n "$TARGET_URL" ]] || die "Missing repo URL. Usage: screen-repo <github-url> [--destroy] [--fresh]"
# --- Check Prerequisites ---
command -v gh >/dev/null 2>&1 || die "GitHub CLI (gh) not found. Install: https://cli.github.com"
# --- Switch to personal account (gradigit) ---
GH_ACCOUNT="gradigit"
CURRENT_USER=$(gh api user --jq .login 2>/dev/null || echo "")
if [[ "$CURRENT_USER" != "$GH_ACCOUNT" ]]; then
echo "Switching GitHub account to $GH_ACCOUNT..."
gh auth switch --user "$GH_ACCOUNT" 2>/dev/null || die "Failed to switch to $GH_ACCOUNT. Run: gh auth login"
fi
gh auth status >/dev/null 2>&1 || die "Not logged in to GitHub CLI. Run: gh auth login"
# --- Get GitHub Username + Ensure Reports Repo ---
GH_USER="$GH_ACCOUNT"
gh repo view "$GH_USER/$REPO_NAME" >/dev/null 2>&1 || {
echo "Creating private repo: $GH_USER/$REPO_NAME"
gh repo create "$REPO_NAME" --private --description "Screening reports from screen.sh"
}
# --- Find or Create Codespace ---
CODESPACE_NAME=""
if [[ "$FRESH" == false ]]; then
# Look for existing Codespace with our display name
CODESPACE_NAME=$(gh codespace list --json name,displayName --jq ".[] | select(.displayName==\"$DISPLAY_NAME\") | .name" 2>/dev/null | head -1)
fi
if [[ -n "$CODESPACE_NAME" ]]; then
echo "Reusing Codespace: $CODESPACE_NAME"
else
echo "Creating new Codespace from $GH_USER/$REPO_NAME..."
CREATE_OUTPUT=$(gh codespace create --repo "$GH_USER/$REPO_NAME" -m "$MACHINE_TYPE" --display-name "$DISPLAY_NAME" 2>&1)
# gh codespace create prints the codespace name on stdout
CODESPACE_NAME=$(echo "$CREATE_OUTPUT" | tail -1)
[[ -n "$CODESPACE_NAME" ]] || die "Failed to create Codespace. Output:\n$CREATE_OUTPUT"
echo "Created Codespace: $CODESPACE_NAME"
fi
# --- Idempotent Provisioning ---
# Only installs Claude Code CLI on first run.
# Screening skill is always updated (pull latest) on every run.
echo "Checking provisioning status..."
INSTALLED=$(gh codespace ssh -c "$CODESPACE_NAME" -- "test -f ~/$MARKER && echo yes || echo no" 2>/dev/null || echo "no")
if [[ "$INSTALLED" != "yes" ]]; then
echo "Installing Claude Code CLI + screening skill..."
gh codespace ssh -c "$CODESPACE_NAME" -- 'bash -s' <<'PROVISION'
set -e
# Claude Code CLI
npm install -g @anthropic-ai/claude-code
# Screening skill
mkdir -p ~/.claude/skills
git clone https://github.com/gradigit/screening-github-cloud ~/.claude/skills/screening-github-cloud
# Alias claude to skip permissions (sandbox is the protection)
grep -q 'alias claude=' ~/.bashrc 2>/dev/null || {
echo 'alias claude="claude --dangerously-skip-permissions"' >> ~/.bashrc
}
# Mark as installed
touch ~/.screening-tools-installed
echo ""
echo "========================================"
echo " Claude Code + screening skill installed"
echo "========================================"
PROVISION
echo "Provisioning complete."
else
echo "Already provisioned. Updating screening skill..."
gh codespace ssh -c "$CODESPACE_NAME" -- 'bash -s' <<'UPDATE'
set -e
if [ -d ~/.claude/skills/screening-github-cloud/.git ]; then
git -C ~/.claude/skills/screening-github-cloud pull --ff-only
echo "Skill updated."
else
mkdir -p ~/.claude/skills
git clone https://github.com/gradigit/screening-github-cloud ~/.claude/skills/screening-github-cloud
echo "Skill installed."
fi
UPDATE
fi
# --- Open Interactive SSH ---
echo ""
echo "========================================"
echo " Opening SSH session to Codespace"
echo "========================================"
echo ""
echo " Inside the Codespace, run:"
echo ""
echo " claude \"screen $TARGET_URL\""
echo ""
echo " First time? Run 'claude login' first."
echo " Private repo? Run: unset GITHUB_TOKEN && gh auth login -s repo"
echo ""
echo " Type 'exit' when done."
echo "========================================"
echo ""
gh codespace ssh -c "$CODESPACE_NAME"
# --- Show Report Link ---
# Extract owner/repo from target URL → expected report filename
TARGET_PATH="${TARGET_URL#https://github.com/}"
TARGET_PATH="${TARGET_PATH%.git}"
OWNER_REPO=$(echo "$TARGET_PATH" | tr '/' '-')
TODAY=$(date +%Y-%m-%d)
REPORT_FILE="reports/${TODAY}-${OWNER_REPO}.md"
REPORT_URL="https://github.com/$GH_USER/$REPO_NAME/blob/main/$REPORT_FILE"
# Check if report was actually pushed
if gh api "repos/$GH_USER/$REPO_NAME/contents/$REPORT_FILE" >/dev/null 2>&1; then
echo ""
echo "========================================"
echo " Report: $REPORT_URL"
echo "========================================"
fi
# --- Stop or Destroy ---
if [[ "$DESTROY" == true ]]; then
echo "Destroying Codespace: $CODESPACE_NAME"
gh codespace delete -c "$CODESPACE_NAME" --force
echo "Codespace deleted."
else
echo "Stopping Codespace: $CODESPACE_NAME"
gh codespace stop -c "$CODESPACE_NAME" 2>/dev/null || true
echo "Codespace stopped (will be reused on next run)."
echo "To delete: gh codespace delete -c $CODESPACE_NAME --force"
fi