seamshield 0.0.1 → 0.1.1
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 +124 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1096 -0
- package/package.json +58 -4
- package/rules/secrets.patterns.yaml +28 -0
- package/rules/ss-agent-mcp-inline-credentials.yaml +23 -0
- package/rules/ss-agent-overbroad-permissions.yaml +27 -0
- package/rules/ss-agent-secrets-in-agent-files.yaml +22 -0
- package/rules/ss-auth-admin-route-unprotected.yaml +24 -0
- package/rules/ss-auth-api-route-no-auth.yaml +24 -0
- package/rules/ss-auth-client-only-guard.yaml +24 -0
- package/rules/ss-auth-cors-wildcard-with-credentials.yaml +21 -0
- package/rules/ss-client-firebase-admin-in-client.yaml +23 -0
- package/rules/ss-client-next-public-secret.yaml +33 -0
- package/rules/ss-client-server-secret-env-in-client.yaml +24 -0
- package/rules/ss-client-supabase-service-role-in-client.yaml +24 -0
- package/rules/ss-convex-internal-not-internal.yaml +25 -0
- package/rules/ss-convex-mutation-no-auth.yaml +26 -0
- package/rules/ss-deps-hallucinated-package.yaml +16 -0
- package/rules/ss-deps-known-vuln.yaml +16 -0
- package/rules/ss-deps-no-lockfile.yaml +17 -0
- package/rules/ss-deps-unpinned-spec.yaml +22 -0
- package/rules/ss-firebase-open-rules.yaml +22 -0
- package/rules/ss-secrets-env-file-committed.yaml +21 -0
- package/rules/ss-secrets-generic-credential-assignment.yaml +26 -0
- package/rules/ss-secrets-hardcoded-provider-key.yaml +47 -0
- package/rules/ss-secrets-private-key-file.yaml +22 -0
- package/rules/ss-secrets-supabase-service-role-key.yaml +25 -0
- package/rules/ss-supabase-permissive-policy.yaml +22 -0
- package/rules/ss-supabase-rls-disabled.yaml +22 -0
- package/schemas/finding.schema.json +92 -0
- package/index.js +0 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
id: ss/secrets/supabase-service-role-key
|
|
2
|
+
severity: block
|
|
3
|
+
title: Supabase service-role key hardcoded in source
|
|
4
|
+
description: >
|
|
5
|
+
A Supabase service-role JWT is written directly in source code. The
|
|
6
|
+
service-role key bypasses Row Level Security entirely — anyone holding it
|
|
7
|
+
can read and write every row in the database.
|
|
8
|
+
framework_ref: AI_AGENT_DROP_IN.md#default-security-stance
|
|
9
|
+
check:
|
|
10
|
+
type: regex
|
|
11
|
+
redact: true
|
|
12
|
+
include:
|
|
13
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json", ".yaml", ".yml", ".toml", ".html", ".svelte", ".vue", ".astro", ".md"]
|
|
14
|
+
exclude:
|
|
15
|
+
basenames: [".env*", "package-lock.json", "pnpm-lock.yaml", "yarn.lock", "*.min.js"]
|
|
16
|
+
patterns:
|
|
17
|
+
- name: supabase-service-role-jwt
|
|
18
|
+
regex: "eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]*(?:c2VydmljZV9yb2xl|NlcnZpY2Vfcm9sZ|zZXJ2aWNlX3JvbG)[A-Za-z0-9_-]*\\.[A-Za-z0-9_-]+"
|
|
19
|
+
fix:
|
|
20
|
+
summary: Remove the service-role key from source, keep it server-side only, and rotate it.
|
|
21
|
+
agent_prompt: >
|
|
22
|
+
Delete this hardcoded Supabase service-role key. Read it from a
|
|
23
|
+
server-only environment variable instead, never expose it to client
|
|
24
|
+
code, and tell the user to rotate the key in the Supabase dashboard
|
|
25
|
+
(Settings > API) because the committed value is burned.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: ss/supabase/permissive-policy
|
|
2
|
+
severity: high
|
|
3
|
+
title: RLS policy that allows everything
|
|
4
|
+
description: >
|
|
5
|
+
A policy with USING (true) or WITH CHECK (true) grants the operation to
|
|
6
|
+
every requester, including the public anon role. RLS is technically on,
|
|
7
|
+
but it protects nothing.
|
|
8
|
+
framework_ref: AI_AGENT_DROP_IN.md#default-security-stance
|
|
9
|
+
check:
|
|
10
|
+
type: regex
|
|
11
|
+
include:
|
|
12
|
+
extensions: [".sql"]
|
|
13
|
+
patterns:
|
|
14
|
+
- name: policy-always-true
|
|
15
|
+
regex: "(?:USING|using|WITH CHECK|with check)\\s*\\(\\s*(?:true|TRUE)\\s*\\)"
|
|
16
|
+
fix:
|
|
17
|
+
summary: Scope the policy to the requesting user (for example auth.uid() = user_id).
|
|
18
|
+
agent_prompt: >
|
|
19
|
+
Rewrite this policy condition to scope access to the requesting user —
|
|
20
|
+
typically `auth.uid() = user_id` for ownership, or a role check for
|
|
21
|
+
shared data. If the table is genuinely public read-only, restrict the
|
|
22
|
+
policy to SELECT and add a comment explaining why.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: ss/supabase/rls-disabled
|
|
2
|
+
severity: block
|
|
3
|
+
title: Row Level Security disabled on a table
|
|
4
|
+
description: >
|
|
5
|
+
A SQL migration disables Row Level Security. On Supabase the anon key is
|
|
6
|
+
public by design — RLS is the only thing standing between the internet
|
|
7
|
+
and this table's rows.
|
|
8
|
+
framework_ref: AI_AGENT_DROP_IN.md#default-security-stance
|
|
9
|
+
check:
|
|
10
|
+
type: regex
|
|
11
|
+
include:
|
|
12
|
+
extensions: [".sql"]
|
|
13
|
+
patterns:
|
|
14
|
+
- name: rls-disabled
|
|
15
|
+
regex: "(?:DISABLE ROW LEVEL SECURITY|disable row level security)"
|
|
16
|
+
fix:
|
|
17
|
+
summary: Re-enable RLS and write policies for the access the app actually needs.
|
|
18
|
+
agent_prompt: >
|
|
19
|
+
Change this statement to ENABLE ROW LEVEL SECURITY and add explicit
|
|
20
|
+
CREATE POLICY statements for each access pattern the app needs (for
|
|
21
|
+
example: users can select/update their own rows). If RLS was disabled
|
|
22
|
+
to debug a policy, fix the policy instead of removing the protection.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://seamshield.dev/schemas/finding.schema.json",
|
|
4
|
+
"title": "SeamShield Scan Finding",
|
|
5
|
+
"description": "Profile of seamshield-final-framework canonical-security-ir for scanner findings, extended with a `finding` object. Spine fields keep canonical IR semantics.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"event_id",
|
|
9
|
+
"event_type",
|
|
10
|
+
"time",
|
|
11
|
+
"tenant",
|
|
12
|
+
"decision",
|
|
13
|
+
"route",
|
|
14
|
+
"engines",
|
|
15
|
+
"provenance",
|
|
16
|
+
"spans",
|
|
17
|
+
"finding"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"event_id": { "type": "string", "minLength": 8 },
|
|
21
|
+
"event_type": { "const": "scan.finding" },
|
|
22
|
+
"time": { "type": "string", "format": "date-time" },
|
|
23
|
+
"tenant": { "type": "string" },
|
|
24
|
+
"decision": { "enum": ["deny", "scan"] },
|
|
25
|
+
"route": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["plane", "lane", "reason"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"plane": { "const": "evidence" },
|
|
30
|
+
"lane": { "const": "cpu" },
|
|
31
|
+
"reason": {
|
|
32
|
+
"type": "array",
|
|
33
|
+
"items": { "type": "string" },
|
|
34
|
+
"minItems": 1
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"minItems": 1,
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"required": ["name", "version"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"name": { "type": "string" },
|
|
46
|
+
"version": { "type": "string" },
|
|
47
|
+
"role": { "type": "string" }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"provenance": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"required": ["policy_bundle_digest"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"policy_bundle_digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"spans": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"required": ["start", "end", "label", "evidence"],
|
|
63
|
+
"properties": {
|
|
64
|
+
"start": { "type": "integer", "minimum": 1 },
|
|
65
|
+
"end": { "type": "integer", "minimum": 1 },
|
|
66
|
+
"label": { "type": "string" },
|
|
67
|
+
"evidence": { "type": "string" }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"finding": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["rule_id", "severity", "title", "file", "line", "fix"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"rule_id": { "type": "string", "pattern": "^ss/[a-z-]+/[a-z0-9-]+$" },
|
|
76
|
+
"severity": { "enum": ["block", "high", "warn", "info"] },
|
|
77
|
+
"title": { "type": "string" },
|
|
78
|
+
"file": { "type": "string" },
|
|
79
|
+
"line": { "type": "integer", "minimum": 1 },
|
|
80
|
+
"fix": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"required": ["summary", "agent_prompt"],
|
|
83
|
+
"properties": {
|
|
84
|
+
"summary": { "type": "string" },
|
|
85
|
+
"agent_prompt": { "type": "string" },
|
|
86
|
+
"doc_url": { "type": "string" }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/index.js
DELETED