Skip to content

Commit d17603c

Browse files
authored
chore: sync template, enable curly rule, add tsgo typecheck (#1203)
* chore: sync hooks and skills from socket-repo-template * chore: remove socket-registry/CLAUDE.md cross-reference * chore: sync hooks and skills from socket-repo-template * chore: enable eslint/curly rule to require braces on all control flow * chore: update @typescript/native-preview to 7.0.0-dev.20260415.1 * feat: add "typecheck" script using tsgo (typescript-go) * revert: remove standalone typecheck script * fix: use word boundaries in AWS key detection to avoid base64 false positives * feat: add tsgo type checking to check runner (lint + format + typecheck) * fix: skip binary files in personal path detection * fix: restore personal path detection for binary files
1 parent 8fc3518 commit d17603c

File tree

11 files changed

+155
-44
lines changed

11 files changed

+155
-44
lines changed

.claude/skills/_shared/security-tools.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ No install step needed — available after `pnpm install`.
1111
## Zizmor
1212

1313
Not an npm package. Installed via `pnpm run setup` which downloads the pinned version
14-
from GitHub releases with SHA256 checksum verification (see `bundle-tools.json`).
14+
from GitHub releases with SHA256 checksum verification (see `external-tools.json`).
1515

1616
The binary is cached at `.cache/external-tools/zizmor/{version}-{platform}/zizmor`.
1717

.claude/skills/security-scan/SKILL.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
---
22
name: security-scan
33
description: Runs a multi-tool security scan — AgentShield for Claude config, zizmor for GitHub Actions, and optionally Socket CLI for dependency scanning. Produces an A-F graded security report.
4+
user-invocable: true
45
---
56

67
# Security Scan
78

89
Multi-tool security scanning pipeline for the repository.
910

10-
## Related: check-new-deps Hook
11-
12-
This repo includes a pre-tool hook (`.claude/hooks/check-new-deps/`) that automatically
13-
checks new dependencies against Socket.dev's malware API before Claude adds them.
14-
The hook runs on every Edit/Write to manifest files — see its README for details.
15-
This skill covers broader security scanning; the hook provides real-time dependency protection.
16-
1711
## When to Use
1812

1913
- After modifying `.claude/` config, settings, hooks, or agent definitions

.git-hooks/commit-msg

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ if [ -n "$COMMITTED_FILES" ]; then
2323
if [ -f "$file" ]; then
2424
# Check for Socket API keys (except allowed).
2525
if grep -E 'sktsec_[a-zA-Z0-9_-]+' "$file" 2>/dev/null | grep -v "$ALLOWED_PUBLIC_KEY" | grep -v 'your_api_key_here' | grep -v 'fake-token' | grep -v 'test-token' | grep -v '\.example' | grep -q .; then
26-
echo "${RED}✗ SECURITY: Potential API key detected in commit!${NC}"
26+
printf "${RED}✗ SECURITY: Potential API key detected in commit!${NC}\n"
2727
printf "File: %s\n" "$file"
2828
ERRORS=$((ERRORS + 1))
2929
fi
3030

3131
# Check for .env files.
3232
if echo "$file" | grep -qE '^\.env(\.local)?$'; then
33-
echo "${RED}✗ SECURITY: .env file in commit!${NC}"
33+
printf "${RED}✗ SECURITY: .env file in commit!${NC}\n"
3434
ERRORS=$((ERRORS + 1))
3535
fi
3636
fi
@@ -41,7 +41,12 @@ fi
4141
COMMIT_MSG_FILE="$1"
4242
if [ -f "$COMMIT_MSG_FILE" ]; then
4343
# Create a temporary file to store the cleaned message.
44-
TEMP_FILE=$(mktemp)
44+
TEMP_FILE=$(mktemp) || {
45+
printf "${RED}✗ Failed to create temporary file${NC}\n" >&2
46+
exit 1
47+
}
48+
# Ensure cleanup on exit
49+
trap 'rm -f "$TEMP_FILE"' EXIT
4550
REMOVED_LINES=0
4651

4752
# Read the commit message line by line and filter out AI attribution.
@@ -58,15 +63,15 @@ if [ -f "$COMMIT_MSG_FILE" ]; then
5863
# Replace the original commit message with the cleaned version.
5964
if [ $REMOVED_LINES -gt 0 ]; then
6065
mv "$TEMP_FILE" "$COMMIT_MSG_FILE"
61-
echo "${GREEN}✓ Auto-stripped${NC} $REMOVED_LINES AI attribution line(s) from commit message"
66+
printf "${GREEN}✓ Auto-stripped${NC} $REMOVED_LINES AI attribution line(s) from commit message\n"
6267
else
6368
# No lines were removed, just clean up the temp file.
6469
rm -f "$TEMP_FILE"
6570
fi
6671
fi
6772

6873
if [ $ERRORS -gt 0 ]; then
69-
echo "${RED}✗ Commit blocked by security validation${NC}"
74+
printf "${RED}✗ Commit blocked by security validation${NC}\n"
7075
exit 1
7176
fi
7277

.git-hooks/pre-push

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ while read local_ref local_sha remote_ref remote_sha; do
154154
file_text=$(cat "$file" 2>/dev/null)
155155
fi
156156

157-
# Hardcoded personal paths (/Users/foo/, /home/foo/, C:\Users\foo\).
157+
# Hardcoded personal paths (/Users/foo/, /home/foo/, C:\\Users\\foo\\).
158158
if echo "$file_text" | grep -qE '(/Users/[^/\s]+/|/home/[^/\s]+/|C:\\Users\\[^\\]+\\)'; then
159159
printf "${RED}✗ BLOCKED: Hardcoded personal path found in: %s${NC}\n" "$file"
160160
echo "$file_text" | grep -nE '(/Users/[^/\s]+/|/home/[^/\s]+/|C:\\Users\\[^\\]+\\)' | head -3
@@ -168,10 +168,10 @@ while read local_ref local_sha remote_ref remote_sha; do
168168
ERRORS=$((ERRORS + 1))
169169
fi
170170

171-
# AWS keys.
172-
if echo "$file_text" | grep -iqE '(aws_access_key|aws_secret|AKIA[0-9A-Z]{16})'; then
171+
# AWS keys (word-boundary match to avoid false positives in base64 data).
172+
if echo "$file_text" | grep -iqE '(aws_access_key|aws_secret|\bAKIA[0-9A-Z]{16}\b)'; then
173173
printf "${RED}✗ BLOCKED: Potential AWS credentials found in: %s${NC}\n" "$file"
174-
echo "$file_text" | grep -niE '(aws_access_key|aws_secret|AKIA[0-9A-Z]{16})' | head -3
174+
echo "$file_text" | grep -niE '(aws_access_key|aws_secret|\bAKIA[0-9A-Z]{16}\b)' | head -3
175175
ERRORS=$((ERRORS + 1))
176176
fi
177177

.husky/commit-msg

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
# Run commit message validation and auto-strip AI attribution.
2-
.git-hooks/commit-msg "$1"
2+
if [ -x ".git-hooks/commit-msg" ]; then
3+
.git-hooks/commit-msg "$1"
4+
else
5+
printf "\033[0;31m✗ Error: .git-hooks/commit-msg not found or not executable\033[0m\n" >&2
6+
exit 1
7+
fi

.oxlintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"suspicious": "error"
77
},
88
"rules": {
9-
"eslint/curly": "off",
9+
"eslint/curly": ["error", "all"],
1010
"eslint/no-await-in-loop": "off",
1111
"eslint/no-console": "off",
1212
"eslint/no-control-regex": "off",

CLAUDE.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ If user repeats instruction 2+ times, ask: "Should I add this to CLAUDE.md?"
6868

6969
## SHARED STANDARDS
7070

71-
**Canonical reference**: `../socket-registry/CLAUDE.md`
7271

73-
All shared standards (git, testing, code style, cross-platform, CI) defined in socket-registry/CLAUDE.md.
7472

7573
- Commits: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) `<type>(<scope>): <description>` -- NO AI attribution
7674
- Scripts: Prefer `pnpm run foo --flag` over `foo:bar` scripts

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"pretest": "pnpm run build:cli"
5353
},
5454
"devDependencies": {
55+
"@typescript/native-preview": "7.0.0-dev.20260415.1",
5556
"@anthropic-ai/claude-code": "catalog:",
5657
"@babel/core": "catalog:",
5758
"@babel/parser": "catalog:",

packages/cli/scripts/sync-checksums.mjs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@
1717
*/
1818

1919
import { createHash } from 'node:crypto'
20-
import { createReadStream, existsSync, readFileSync, promises as fs } from 'node:fs'
20+
import {
21+
createReadStream,
22+
existsSync,
23+
readFileSync,
24+
promises as fs,
25+
} from 'node:fs'
2126
import os from 'node:os'
2227
import path from 'node:path'
2328
import { fileURLToPath } from 'node:url'
@@ -48,7 +53,9 @@ function parseChecksums(content) {
4853
const checksums = {}
4954
for (const line of content.split('\n')) {
5055
const trimmed = line.trim()
51-
if (!trimmed) continue
56+
if (!trimmed) {
57+
continue
58+
}
5259
// Format: hash filename (two spaces or whitespace between)
5360
const match = trimmed.match(/^([a-f0-9]{64})\s+(.+)$/)
5461
if (match) {
@@ -64,7 +71,7 @@ function parseChecksums(content) {
6471
async function downloadFile(url, destPath) {
6572
const response = await fetch(url, {
6673
headers: {
67-
'Accept': 'application/octet-stream',
74+
Accept: 'application/octet-stream',
6875
'User-Agent': 'socket-cli-sync-checksums',
6976
},
7077
redirect: 'follow',
@@ -87,21 +94,27 @@ async function downloadFile(url, destPath) {
8794
* Fetch checksums for a GitHub release.
8895
* First tries checksums.txt, then falls back to downloading assets.
8996
*/
90-
async function fetchGitHubReleaseChecksums(repo, releaseTag, existingChecksums = {}) {
97+
async function fetchGitHubReleaseChecksums(
98+
repo,
99+
releaseTag,
100+
existingChecksums = {},
101+
) {
91102
const [owner, repoName] = repo.split('/')
92103
const apiUrl = `https://api.github.com/repos/${owner}/${repoName}/releases/tags/${releaseTag}`
93104

94105
console.log(` Fetching release info from ${apiUrl}...`)
95106

96107
const response = await fetch(apiUrl, {
97108
headers: {
98-
'Accept': 'application/vnd.github.v3+json',
109+
Accept: 'application/vnd.github.v3+json',
99110
'User-Agent': 'socket-cli-sync-checksums',
100111
},
101112
})
102113

103114
if (!response.ok) {
104-
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
115+
throw new Error(
116+
`GitHub API error: ${response.status} ${response.statusText}`,
117+
)
105118
}
106119

107120
const release = await response.json()
@@ -111,7 +124,9 @@ async function fetchGitHubReleaseChecksums(repo, releaseTag, existingChecksums =
111124
const checksumsAsset = assets.find(a => a.name === 'checksums.txt')
112125
if (checksumsAsset) {
113126
console.log(` Found checksums.txt, downloading...`)
114-
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'socket-checksums-'))
127+
const tempDir = await fs.mkdtemp(
128+
path.join(os.tmpdir(), 'socket-checksums-'),
129+
)
115130
const checksumPath = path.join(tempDir, 'checksums.txt')
116131

117132
try {
@@ -122,7 +137,9 @@ async function fetchGitHubReleaseChecksums(repo, releaseTag, existingChecksums =
122137
// Clean up.
123138
await fs.rm(tempDir, { recursive: true })
124139

125-
console.log(` Parsed ${Object.keys(checksums).length} checksums from checksums.txt`)
140+
console.log(
141+
` Parsed ${Object.keys(checksums).length} checksums from checksums.txt`,
142+
)
126143
return checksums
127144
} catch (error) {
128145
console.log(` Failed to download checksums.txt: ${error.message}`)
@@ -139,7 +156,9 @@ async function fetchGitHubReleaseChecksums(repo, releaseTag, existingChecksums =
139156
return {}
140157
}
141158

142-
console.log(` No checksums.txt found, downloading ${assetNames.length} assets to compute checksums...`)
159+
console.log(
160+
` No checksums.txt found, downloading ${assetNames.length} assets to compute checksums...`,
161+
)
143162

144163
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'socket-checksums-'))
145164
const checksums = {}
@@ -192,24 +211,32 @@ async function main() {
192211
// Find all GitHub-released tools.
193212
const githubTools = Object.entries(externalTools)
194213
.filter(([key, value]) => {
195-
if (key.startsWith('$')) return false // Skip schema keys
214+
if (key.startsWith('$')) {
215+
return false
216+
} // Skip schema keys
196217
return value.release === 'asset'
197218
})
198219
.map(([key, value]) => ({ key, ...value }))
199220

200221
if (toolFilter) {
201222
const filtered = githubTools.filter(t => t.key === toolFilter)
202223
if (filtered.length === 0) {
203-
console.error(`Error: Tool '${toolFilter}' not found or is not a GitHub release tool`)
204-
console.log(`Available GitHub release tools: ${githubTools.map(t => t.key).join(', ')}`)
224+
console.error(
225+
`Error: Tool '${toolFilter}' not found or is not a GitHub release tool`,
226+
)
227+
console.log(
228+
`Available GitHub release tools: ${githubTools.map(t => t.key).join(', ')}`,
229+
)
205230
process.exitCode = 1
206231
return
207232
}
208233
githubTools.length = 0
209234
githubTools.push(...filtered)
210235
}
211236

212-
console.log(`Syncing checksums for ${githubTools.length} GitHub release tool(s)...\n`)
237+
console.log(
238+
`Syncing checksums for ${githubTools.length} GitHub release tool(s)...\n`,
239+
)
213240

214241
let updated = 0
215242
let unchanged = 0
@@ -235,10 +262,13 @@ async function main() {
235262

236263
// Check if update is needed.
237264
const oldChecksums = tool.checksums || {}
238-
const checksumChanged = JSON.stringify(newChecksums) !== JSON.stringify(oldChecksums)
265+
const checksumChanged =
266+
JSON.stringify(newChecksums) !== JSON.stringify(oldChecksums)
239267

240268
if (!force && !checksumChanged) {
241-
console.log(` Unchanged: ${Object.keys(newChecksums).length} checksums\n`)
269+
console.log(
270+
` Unchanged: ${Object.keys(newChecksums).length} checksums\n`,
271+
)
242272
unchanged++
243273
continue
244274
}
@@ -269,7 +299,9 @@ async function main() {
269299
}
270300

271301
// Summary.
272-
console.log(`\nSummary: ${updated} updated, ${unchanged} unchanged, ${failed} failed`)
302+
console.log(
303+
`\nSummary: ${updated} updated, ${unchanged} unchanged, ${failed} failed`,
304+
)
273305

274306
if (failed > 0) {
275307
process.exitCode = 1

pnpm-lock.yaml

Lines changed: 73 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)