seamshield 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -55
- package/dist/index.js +14 -2
- package/package.json +2 -2
- package/rules/ss-client-next-public-secret.yaml +7 -1
package/README.md
CHANGED
|
@@ -1,32 +1,43 @@
|
|
|
1
1
|
# SeamShield
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Local security scanner for AI-built JavaScript and TypeScript apps.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- `npx seamshield scan` - CLI scanner with table, JSON, and SARIF output.
|
|
8
|
-
- `npx seamshield fix-plan` - writes agent-ready remediation prompts.
|
|
9
|
-
- `npx seamshield agent-context` - writes SeamShield instructions for Claude Code or Cursor.
|
|
10
|
-
- `npx seamshield guard install` / `guard check` - Claude Code PreToolUse guard for insecure edits and commands.
|
|
5
|
+
SeamShield finds common flaws that AI-generated apps often ship: committed secrets, client-exposed server keys, client-only auth, open platform rules, unsafe agent config, and dependency supply-chain risks.
|
|
11
6
|
|
|
12
7
|
## Install
|
|
13
8
|
|
|
14
9
|
```bash
|
|
15
|
-
npm install -g seamshield
|
|
16
|
-
# or
|
|
17
10
|
npx seamshield scan .
|
|
18
11
|
```
|
|
19
12
|
|
|
13
|
+
Or install globally:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g seamshield
|
|
17
|
+
seamshield scan .
|
|
18
|
+
```
|
|
19
|
+
|
|
20
20
|
Requires Node.js 20 or newer.
|
|
21
21
|
|
|
22
|
+
## Commands
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
seamshield scan .
|
|
26
|
+
seamshield fix-plan .
|
|
27
|
+
seamshield agent-context . --claude
|
|
28
|
+
seamshield agent-context . --cursor
|
|
29
|
+
seamshield guard install .
|
|
30
|
+
seamshield guard check
|
|
31
|
+
```
|
|
32
|
+
|
|
22
33
|
## Scan
|
|
23
34
|
|
|
24
35
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
seamshield scan .
|
|
37
|
+
seamshield scan . --format json
|
|
38
|
+
seamshield scan . --format sarif
|
|
39
|
+
seamshield scan . --fail-on high
|
|
40
|
+
seamshield scan . --offline
|
|
30
41
|
```
|
|
31
42
|
|
|
32
43
|
Exit codes:
|
|
@@ -37,24 +48,19 @@ Exit codes:
|
|
|
37
48
|
|
|
38
49
|
`--offline` disables npm registry and OSV checks. Static rules still run.
|
|
39
50
|
|
|
40
|
-
## Fix
|
|
51
|
+
## Fix Plans
|
|
41
52
|
|
|
42
53
|
```bash
|
|
43
|
-
|
|
54
|
+
seamshield fix-plan .
|
|
44
55
|
```
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- the finding list,
|
|
49
|
-
- redacted evidence,
|
|
50
|
-
- per-finding `agent_prompt` text,
|
|
51
|
-
- one combined `agent_markdown` block for Claude Code, Cursor, or another coding agent.
|
|
57
|
+
Writes `.seamshield/fix-plan.json` with redacted findings and agent-ready remediation prompts.
|
|
52
58
|
|
|
53
59
|
## Agent Context
|
|
54
60
|
|
|
55
61
|
```bash
|
|
56
|
-
|
|
57
|
-
|
|
62
|
+
seamshield agent-context . --claude
|
|
63
|
+
seamshield agent-context . --cursor
|
|
58
64
|
```
|
|
59
65
|
|
|
60
66
|
Claude writes or updates `CLAUDE.md`. Cursor writes `.cursor/rules/seamshield.mdc`.
|
|
@@ -62,14 +68,14 @@ Claude writes or updates `CLAUDE.md`. Cursor writes `.cursor/rules/seamshield.md
|
|
|
62
68
|
## Claude Code Guard
|
|
63
69
|
|
|
64
70
|
```bash
|
|
65
|
-
|
|
71
|
+
seamshield guard install .
|
|
66
72
|
```
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
Installs a Claude Code `PreToolUse` hook for `Write`, `Edit`, `MultiEdit`, and `Bash`.
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
The guard denies block-severity edits such as hardcoded provider keys, service-role keys, private keys, committed dotenv files, open Firebase rules, or RLS disablement. Bash checks deny obvious dangerous commands such as `git add .env*`, `curl ... | sh`, and installs of npm packages that do not resolve.
|
|
71
77
|
|
|
72
|
-
Guard behavior is fail-open: if the hook errors, it allows the tool call and appends diagnostics to `.seamshield/guard.log`.
|
|
78
|
+
Guard behavior is fail-open: if the hook errors, it allows the tool call and appends diagnostics to `.seamshield/guard.log`.
|
|
73
79
|
|
|
74
80
|
## Configuration
|
|
75
81
|
|
|
@@ -92,36 +98,21 @@ const fixtureKey = "sk_live_test_fixture_only";
|
|
|
92
98
|
|
|
93
99
|
## Privacy
|
|
94
100
|
|
|
95
|
-
SeamShield
|
|
96
|
-
|
|
97
|
-
## Rule Coverage
|
|
98
|
-
|
|
99
|
-
The rule pack covers:
|
|
101
|
+
SeamShield runs locally. Static scanning does not transmit source code.
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
- Next.js auth footguns,
|
|
103
|
-
- Supabase, Convex, and Firebase platform mistakes,
|
|
104
|
-
- dependency lockfile, pinning, hallucinated-package, and OSV vulnerability checks,
|
|
105
|
-
- agent config secrets and overbroad permissions.
|
|
103
|
+
Network dependency checks send package names and versions to the npm registry and OSV. Use `--offline` to disable those checks.
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
## Development
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
pnpm install
|
|
113
|
-
pnpm build
|
|
114
|
-
pnpm test
|
|
115
|
-
pnpm typecheck
|
|
116
|
-
```
|
|
105
|
+
Secret evidence is redacted before findings, JSON, SARIF, and fix plans are emitted.
|
|
117
106
|
|
|
118
|
-
|
|
107
|
+
## Public Rule Coverage
|
|
119
108
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
109
|
+
- Secrets and client exposure
|
|
110
|
+
- Next.js auth footguns
|
|
111
|
+
- Supabase, Convex, and Firebase platform mistakes
|
|
112
|
+
- Dependency lockfile, pinning, hallucinated-package, and OSV vulnerability checks
|
|
113
|
+
- Agent config secrets and overbroad permissions
|
|
124
114
|
|
|
125
|
-
##
|
|
115
|
+
## Links
|
|
126
116
|
|
|
127
|
-
|
|
117
|
+
- Repository: https://github.com/KaraboGerald/SeamShield
|
|
118
|
+
- Issues: https://github.com/KaraboGerald/SeamShield/issues
|
package/dist/index.js
CHANGED
|
@@ -99,7 +99,7 @@ var require_picocolors = __commonJS({
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
// src/index.ts
|
|
102
|
-
import { spawnSync as
|
|
102
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
103
103
|
import { existsSync as existsSync2, mkdirSync, mkdtempSync, readFileSync as readFileSync6, rmSync, writeFileSync } from "fs";
|
|
104
104
|
import { tmpdir } from "os";
|
|
105
105
|
import { dirname as dirname3, join as join7, resolve as resolve2 } from "path";
|
|
@@ -141,6 +141,7 @@ import { dirname as dirname2, join as join4 } from "path";
|
|
|
141
141
|
import { existsSync } from "fs";
|
|
142
142
|
import { dirname as dirname22, join as join5 } from "path";
|
|
143
143
|
import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync } from "fs";
|
|
144
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
144
145
|
import { join as join6, relative as relative2 } from "path";
|
|
145
146
|
import { z as z3 } from "zod";
|
|
146
147
|
var ConfigSchema = z.object({
|
|
@@ -713,6 +714,8 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
713
714
|
]);
|
|
714
715
|
var MAX_FILE_BYTES = 1e6;
|
|
715
716
|
function walk(root) {
|
|
717
|
+
const gitFiles = gitTrackedAndUnignoredFiles(root);
|
|
718
|
+
if (gitFiles) return gitFiles;
|
|
716
719
|
const files = [];
|
|
717
720
|
const visit = (dir) => {
|
|
718
721
|
let entries;
|
|
@@ -743,6 +746,15 @@ function walk(root) {
|
|
|
743
746
|
visit(root);
|
|
744
747
|
return files;
|
|
745
748
|
}
|
|
749
|
+
function gitTrackedAndUnignoredFiles(root) {
|
|
750
|
+
const result = spawnSync2(
|
|
751
|
+
"git",
|
|
752
|
+
["-C", root, "ls-files", "--cached", "--others", "--exclude-standard"],
|
|
753
|
+
{ encoding: "utf8", timeout: 1500 }
|
|
754
|
+
);
|
|
755
|
+
if (result.error || result.status !== 0 || typeof result.stdout !== "string") return null;
|
|
756
|
+
return result.stdout.split("\n").filter(Boolean).filter((rel) => !rel.split("/").some((part) => SKIP_DIRS.has(part))).filter((rel) => rel !== ".claude/worktrees" && !rel.includes("/.claude/worktrees/")).filter((rel) => rel !== ".worktrees" && !rel.startsWith(".worktrees/")).map((rel) => ({ abs: join6(root, rel), rel }));
|
|
757
|
+
}
|
|
746
758
|
var FileCache = class {
|
|
747
759
|
cache = /* @__PURE__ */ new Map();
|
|
748
760
|
read(abs) {
|
|
@@ -1013,7 +1025,7 @@ function bashDecision(command) {
|
|
|
1013
1025
|
const name = command.match(/npm\s+(?:i|install|add)\s+(@?[\w.-]+\/?[\w.-]*)/)?.[1];
|
|
1014
1026
|
if (name) {
|
|
1015
1027
|
const encoded = name.startsWith("@") ? name.replace("/", "%2F") : name;
|
|
1016
|
-
const res =
|
|
1028
|
+
const res = spawnSync3("curl", ["-fsSI", `https://registry.npmjs.org/${encoded}`], {
|
|
1017
1029
|
encoding: "utf8",
|
|
1018
1030
|
timeout: 750
|
|
1019
1031
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "seamshield",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Security scanner for AI-generated apps: finds the flaws vibecoded projects predictably ship",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"node": ">=20"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
|
-
"build": "tsup src/index.ts --format esm --clean && tsc -p tsconfig.build.json && rm -rf rules schemas && cp -R ../rules/rules ../rules/schemas .
|
|
45
|
+
"build": "tsup src/index.ts --format esm --clean && tsc -p tsconfig.build.json && rm -rf rules schemas && cp -R ../rules/rules ../rules/schemas .",
|
|
46
46
|
"prepack": "pnpm run build",
|
|
47
47
|
"test": "vitest run"
|
|
48
48
|
},
|
|
@@ -19,9 +19,15 @@ check:
|
|
|
19
19
|
- ".cjs"
|
|
20
20
|
basenames:
|
|
21
21
|
- ".env*"
|
|
22
|
+
exclude:
|
|
23
|
+
dirs:
|
|
24
|
+
- "scripts"
|
|
25
|
+
- "test"
|
|
26
|
+
- "tests"
|
|
27
|
+
- "__tests__"
|
|
22
28
|
patterns:
|
|
23
29
|
- name: next-public-secret-name
|
|
24
|
-
regex: "NEXT_PUBLIC_[A-Z0-9_]*(?:SECRET|SERVICE_ROLE|PRIVATE|PASSWORD|ADMIN)[A-Z0-9_]*"
|
|
30
|
+
regex: "(?:^\\s*|process\\.env\\.|import\\.meta\\.env\\.)(NEXT_PUBLIC_[A-Z0-9_]*(?:SECRET|SERVICE_ROLE|PRIVATE|PASSWORD|ADMIN)[A-Z0-9_]*)"
|
|
25
31
|
fix:
|
|
26
32
|
summary: Rename the variable to drop the NEXT_PUBLIC_ prefix and read it only on the server.
|
|
27
33
|
agent_prompt: >
|