@prave/shared 1.2.2 → 1.4.0
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/dist/lib/compile-skill.d.ts +54 -0
- package/dist/lib/compile-skill.d.ts.map +1 -0
- package/dist/lib/compile-skill.js +52 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +2 -0
- package/dist/lib/secret-scanner.d.ts +63 -0
- package/dist/lib/secret-scanner.d.ts.map +1 -0
- package/dist/lib/secret-scanner.js +214 -0
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/run.schema.d.ts +501 -0
- package/dist/schemas/run.schema.d.ts.map +1 -0
- package/dist/schemas/run.schema.js +179 -0
- package/dist/schemas/skill.schema.d.ts +51 -0
- package/dist/schemas/skill.schema.d.ts.map +1 -1
- package/dist/schemas/skill.schema.js +42 -0
- package/dist/types/plan-limits.d.ts +27 -0
- package/dist/types/plan-limits.d.ts.map +1 -1
- package/dist/types/plan-limits.js +26 -4
- package/package.json +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { AgentType } from '../types/intelligence.js';
|
|
2
|
+
/**
|
|
3
|
+
* compileSkill — convert a single canonical `SKILL.md` body into the
|
|
4
|
+
* exact file shape each supported agent expects.
|
|
5
|
+
*
|
|
6
|
+
* Why this lives in `@prave/shared`:
|
|
7
|
+
* - The CLI's `prave deploy` writes the converted file straight to
|
|
8
|
+
* the agent's local directory.
|
|
9
|
+
* - The SaaS `/dashboard/compile` page bundles converted files into
|
|
10
|
+
* a zip download.
|
|
11
|
+
* - Both need byte-identical output, otherwise users see drift
|
|
12
|
+
* between "what `prave deploy` puts on disk" and "what the
|
|
13
|
+
* compile page generates". Putting the converters in a shared
|
|
14
|
+
* package is the only way to guarantee that.
|
|
15
|
+
*
|
|
16
|
+
* Per-agent conversion rules (current 6 supported, others stubbed):
|
|
17
|
+
*
|
|
18
|
+
* claude / cline / amp / gemini / codex
|
|
19
|
+
* Identity transform. The canonical SKILL.md format IS the
|
|
20
|
+
* Anthropic skill format; these agents either use it natively or
|
|
21
|
+
* accept it via a `~/.<agent>/skills/<slug>/SKILL.md` directory
|
|
22
|
+
* convention. Returned `relPath`s mirror that layout.
|
|
23
|
+
*
|
|
24
|
+
* cursor
|
|
25
|
+
* Cursor's rules format is `.cursor/rules/<slug>.mdc` with
|
|
26
|
+
* frontmatter `globs:` instead of `triggers:`. We rename the key
|
|
27
|
+
* when frontmatter is present; when absent, we synthesize a
|
|
28
|
+
* minimal frontmatter block so the .mdc file parses cleanly. The
|
|
29
|
+
* body is left untouched.
|
|
30
|
+
*
|
|
31
|
+
* The `path` returned is a **POSIX-style relative path** rooted at the
|
|
32
|
+
* agent's install dir. The CLI joins it onto the expanded `basePath`
|
|
33
|
+
* (e.g. `~/.claude/skills/`); the web zip uses it as the in-archive
|
|
34
|
+
* path. Never absolute, never platform-specific separators — that's
|
|
35
|
+
* the caller's job.
|
|
36
|
+
*/
|
|
37
|
+
export interface CompiledArtifact {
|
|
38
|
+
agent: AgentType;
|
|
39
|
+
/** Relative path within the agent's install root. POSIX separators. */
|
|
40
|
+
path: string;
|
|
41
|
+
/** File body to write at `path`. UTF-8 text. */
|
|
42
|
+
content: string;
|
|
43
|
+
/**
|
|
44
|
+
* `true` when the body was rewritten (frontmatter rename, key
|
|
45
|
+
* synthesis, format mutation). UI surfaces this as a small
|
|
46
|
+
* "converted" badge so users know the agent gets a non-canonical
|
|
47
|
+
* shape. `false` for identity-transformed agents.
|
|
48
|
+
*/
|
|
49
|
+
converted: boolean;
|
|
50
|
+
}
|
|
51
|
+
export declare function compileSkill(content: string, slug: string, agent: AgentType): CompiledArtifact;
|
|
52
|
+
/** Bundle helper — compile a skill for many agents in one pass. */
|
|
53
|
+
export declare function compileSkillBundle(content: string, slug: string, agents: ReadonlyArray<AgentType>): CompiledArtifact[];
|
|
54
|
+
//# sourceMappingURL=compile-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-skill.d.ts","sourceRoot":"","sources":["../../src/lib/compile-skill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,SAAS,CAAA;IAChB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,GACf,gBAAgB,CAWlB;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,GAC/B,gBAAgB,EAAE,CAEpB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function compileSkill(content, slug, agent) {
|
|
2
|
+
switch (agent) {
|
|
3
|
+
case 'cursor':
|
|
4
|
+
return compileCursor(content, slug);
|
|
5
|
+
case 'claude':
|
|
6
|
+
case 'codex':
|
|
7
|
+
case 'gemini':
|
|
8
|
+
case 'cline':
|
|
9
|
+
case 'amp':
|
|
10
|
+
return compileIdentity(content, slug, agent);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Bundle helper — compile a skill for many agents in one pass. */
|
|
14
|
+
export function compileSkillBundle(content, slug, agents) {
|
|
15
|
+
return agents.map((a) => compileSkill(content, slug, a));
|
|
16
|
+
}
|
|
17
|
+
/* ─── per-agent converters ─────────────────────────────────────── */
|
|
18
|
+
function compileIdentity(content, slug, agent) {
|
|
19
|
+
return {
|
|
20
|
+
agent,
|
|
21
|
+
path: `${slug}/SKILL.md`,
|
|
22
|
+
content,
|
|
23
|
+
converted: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function compileCursor(content, slug) {
|
|
27
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
28
|
+
let out;
|
|
29
|
+
if (fmMatch && fmMatch[1] !== undefined) {
|
|
30
|
+
const body = content.slice(fmMatch[0].length);
|
|
31
|
+
// Cursor's spec uses `globs:` not `triggers:`. Other fields
|
|
32
|
+
// (name, description, tags) pass through unchanged. We rename
|
|
33
|
+
// line-anchored so a `triggers:` substring inside an example
|
|
34
|
+
// code block in the body isn't accidentally rewritten.
|
|
35
|
+
const fmInner = fmMatch[1].replace(/(^|\n)triggers:/g, '$1globs:');
|
|
36
|
+
out = `---\n${fmInner}\n---\n${body}`;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
// No frontmatter at all — synthesize a minimal one from the slug
|
|
40
|
+
// and the first non-blank line of the body. Cursor refuses .mdc
|
|
41
|
+
// files without frontmatter, so this is the difference between
|
|
42
|
+
// a working rule and a broken one.
|
|
43
|
+
const firstLine = content.trim().replace(/\s+/g, ' ').slice(0, 200);
|
|
44
|
+
out = `---\nname: ${slug}\ndescription: ${firstLine}\n---\n${content}`;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
agent: 'cursor',
|
|
48
|
+
path: `.cursor/rules/${slug}.mdc`,
|
|
49
|
+
content: out,
|
|
50
|
+
converted: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA"}
|
package/dist/lib/index.js
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret-scanner — shared pre-insert gate for skill bundles.
|
|
3
|
+
*
|
|
4
|
+
* Two layers:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Path patterns** — file names / paths that are categorically
|
|
7
|
+
* forbidden in a public-facing skill bundle. Hits are an immediate
|
|
8
|
+
* reject, no content peek needed.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Content regexes** — high-confidence credential shapes that
|
|
11
|
+
* stand out even inside a text body. We only run these against
|
|
12
|
+
* files that look like text (we don't want to false-match a key
|
|
13
|
+
* shape inside a compressed binary blob).
|
|
14
|
+
*
|
|
15
|
+
* The scanner is called from two places:
|
|
16
|
+
*
|
|
17
|
+
* • `apps/worker/src/jobs/scan-github-repo.job.ts` — silent reject,
|
|
18
|
+
* bundle is marked 'rejected' and never surfaces in the catalogue.
|
|
19
|
+
* • `apps/api/src/services/bundle-storage.service.ts` — surfaces the
|
|
20
|
+
* finding back to the user so they can scrub the file and re-deploy.
|
|
21
|
+
*
|
|
22
|
+
* Same code, two failure modes. Keep this file dependency-free so the
|
|
23
|
+
* worker (Node) and the API (Node) and a future web-side validator
|
|
24
|
+
* (browser) can all run it without bundling friction.
|
|
25
|
+
*/
|
|
26
|
+
export interface SecretFinding {
|
|
27
|
+
/** Path of the offending file within the bundle. */
|
|
28
|
+
path: string;
|
|
29
|
+
/** Short identifier of the rule that fired (`env-file`, `aws-key`, …). */
|
|
30
|
+
rule: string;
|
|
31
|
+
/**
|
|
32
|
+
* 1-indexed line where the match was found, when a content regex
|
|
33
|
+
* fired. Omitted for path-based rejects.
|
|
34
|
+
*/
|
|
35
|
+
line?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface SecretScanInput {
|
|
38
|
+
/** File path relative to the bundle root, e.g. `scripts/post.ts`. */
|
|
39
|
+
path: string;
|
|
40
|
+
/** File content as a UTF-8 string. NULL/undefined for binary files. */
|
|
41
|
+
content?: string | null;
|
|
42
|
+
}
|
|
43
|
+
export interface SecretScanResult {
|
|
44
|
+
/** Final verdict. `clean` means the bundle is safe to expose / run. */
|
|
45
|
+
status: 'clean' | 'rejected';
|
|
46
|
+
/** Every triggered rule. Empty when `status === 'clean'`. */
|
|
47
|
+
findings: SecretFinding[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Run both layers across a list of files. Returns a verdict + every
|
|
51
|
+
* matching rule across every file. Empty findings = clean bundle.
|
|
52
|
+
*
|
|
53
|
+
* Files where `content` is missing skip the content-regex stage but
|
|
54
|
+
* still trip path rules.
|
|
55
|
+
*/
|
|
56
|
+
export declare function scanForSecrets(files: SecretScanInput[]): SecretScanResult;
|
|
57
|
+
/**
|
|
58
|
+
* Whether a path likely points at a text-readable file (worth running
|
|
59
|
+
* content regexes against). Used by callers to filter the input list
|
|
60
|
+
* before they bother loading binary blobs.
|
|
61
|
+
*/
|
|
62
|
+
export declare function isLikelyTextPath(path: string): boolean;
|
|
63
|
+
//# sourceMappingURL=secret-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-scanner.d.ts","sourceRoot":"","sources":["../../src/lib/secret-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,MAAM,EAAE,OAAO,GAAG,UAAU,CAAA;IAC5B,6DAA6D;IAC7D,QAAQ,EAAE,aAAa,EAAE,CAAA;CAC1B;AAwGD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,gBAAgB,CAmCzE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAStD"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret-scanner — shared pre-insert gate for skill bundles.
|
|
3
|
+
*
|
|
4
|
+
* Two layers:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Path patterns** — file names / paths that are categorically
|
|
7
|
+
* forbidden in a public-facing skill bundle. Hits are an immediate
|
|
8
|
+
* reject, no content peek needed.
|
|
9
|
+
*
|
|
10
|
+
* 2. **Content regexes** — high-confidence credential shapes that
|
|
11
|
+
* stand out even inside a text body. We only run these against
|
|
12
|
+
* files that look like text (we don't want to false-match a key
|
|
13
|
+
* shape inside a compressed binary blob).
|
|
14
|
+
*
|
|
15
|
+
* The scanner is called from two places:
|
|
16
|
+
*
|
|
17
|
+
* • `apps/worker/src/jobs/scan-github-repo.job.ts` — silent reject,
|
|
18
|
+
* bundle is marked 'rejected' and never surfaces in the catalogue.
|
|
19
|
+
* • `apps/api/src/services/bundle-storage.service.ts` — surfaces the
|
|
20
|
+
* finding back to the user so they can scrub the file and re-deploy.
|
|
21
|
+
*
|
|
22
|
+
* Same code, two failure modes. Keep this file dependency-free so the
|
|
23
|
+
* worker (Node) and the API (Node) and a future web-side validator
|
|
24
|
+
* (browser) can all run it without bundling friction.
|
|
25
|
+
*/
|
|
26
|
+
// ── Path patterns ────────────────────────────────────────────────────
|
|
27
|
+
// Matched against the lowercased file path. Anchored at the basename
|
|
28
|
+
// where it makes sense (`.env`) and as substring elsewhere (`secrets/`).
|
|
29
|
+
const PATH_RULES = [
|
|
30
|
+
{
|
|
31
|
+
rule: 'env-file',
|
|
32
|
+
test: (p) => {
|
|
33
|
+
const base = basename(p);
|
|
34
|
+
// .env, .env.local, .env.production, .envrc — but NOT .env.example
|
|
35
|
+
// or .env.sample (templates are encouraged).
|
|
36
|
+
if (base === '.env' || base.startsWith('.env.')) {
|
|
37
|
+
return !/\.(example|sample|template)$/.test(base);
|
|
38
|
+
}
|
|
39
|
+
if (base === '.envrc')
|
|
40
|
+
return true;
|
|
41
|
+
return false;
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
rule: 'private-key',
|
|
46
|
+
test: (p) => /\.(pem|p12|pfx|key|jks)$/i.test(p),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
rule: 'ssh-private-key',
|
|
50
|
+
test: (p) => {
|
|
51
|
+
const base = basename(p);
|
|
52
|
+
return /^id_(rsa|ecdsa|ed25519|dsa)$/.test(base);
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
rule: 'gcp-service-account',
|
|
57
|
+
test: (p) => {
|
|
58
|
+
const base = basename(p).toLowerCase();
|
|
59
|
+
return (base === 'service-account.json' ||
|
|
60
|
+
/^service-account[-_].*\.json$/.test(base) ||
|
|
61
|
+
base === 'gcp-key.json' ||
|
|
62
|
+
base === 'firebase-adminsdk.json');
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
rule: 'aws-credentials',
|
|
67
|
+
test: (p) => {
|
|
68
|
+
const lower = p.toLowerCase();
|
|
69
|
+
return (lower.endsWith('/.aws/credentials') ||
|
|
70
|
+
lower === '.aws/credentials' ||
|
|
71
|
+
lower.endsWith('aws-credentials.json'));
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
rule: 'credentials-file',
|
|
76
|
+
test: (p) => {
|
|
77
|
+
const base = basename(p).toLowerCase();
|
|
78
|
+
return (base === 'credentials.json' ||
|
|
79
|
+
base === 'secrets.json' ||
|
|
80
|
+
base === 'secret.json');
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
rule: 'secrets-dir',
|
|
85
|
+
test: (p) => /(^|\/)secrets\//i.test(p),
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
// ── Content regexes ──────────────────────────────────────────────────
|
|
89
|
+
// High-confidence shapes only — false positives mean a frustrated user
|
|
90
|
+
// re-uploading after a scrub they couldn't validate. Keep the bar high.
|
|
91
|
+
const CONTENT_RULES = [
|
|
92
|
+
// Anthropic
|
|
93
|
+
{ rule: 'anthropic-api-key', regex: /\bsk-ant-[a-zA-Z0-9-_]{20,}\b/ },
|
|
94
|
+
// OpenAI
|
|
95
|
+
{ rule: 'openai-api-key', regex: /\bsk-(?:proj-)?[a-zA-Z0-9-_]{32,}\b/ },
|
|
96
|
+
// AWS
|
|
97
|
+
{ rule: 'aws-access-key-id', regex: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
98
|
+
{ rule: 'aws-secret-access-key', regex: /aws_secret_access_key\s*=\s*['"]?[A-Za-z0-9/+=]{40}\b/i },
|
|
99
|
+
// GitHub
|
|
100
|
+
{ rule: 'github-personal-token', regex: /\bghp_[A-Za-z0-9]{36}\b/ },
|
|
101
|
+
{ rule: 'github-oauth-token', regex: /\bgho_[A-Za-z0-9]{36}\b/ },
|
|
102
|
+
{ rule: 'github-fine-grained-token', regex: /\bgithub_pat_[A-Za-z0-9_]{82}\b/ },
|
|
103
|
+
// Stripe
|
|
104
|
+
{ rule: 'stripe-live-key', regex: /\bsk_live_[A-Za-z0-9]{24,}\b/ },
|
|
105
|
+
{ rule: 'stripe-restricted-key', regex: /\brk_live_[A-Za-z0-9]{24,}\b/ },
|
|
106
|
+
// Google API key
|
|
107
|
+
{ rule: 'google-api-key', regex: /\bAIza[0-9A-Za-z\-_]{35}\b/ },
|
|
108
|
+
// Slack
|
|
109
|
+
{ rule: 'slack-bot-token', regex: /\bxoxb-[A-Za-z0-9-]{20,}\b/ },
|
|
110
|
+
{ rule: 'slack-user-token', regex: /\bxoxp-[A-Za-z0-9-]{20,}\b/ },
|
|
111
|
+
// GitLab
|
|
112
|
+
{ rule: 'gitlab-personal-token', regex: /\bglpat-[A-Za-z0-9_-]{20}\b/ },
|
|
113
|
+
// Supabase / JWT-shaped service-role key (the shape, not the host).
|
|
114
|
+
// Has 3 base64url segments separated by '.'. We require an `eyJ`
|
|
115
|
+
// header (RFC 7519 typ:"JWT") to dodge random base64 strings.
|
|
116
|
+
{
|
|
117
|
+
rule: 'jwt-token',
|
|
118
|
+
regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/,
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
/**
|
|
122
|
+
* Run both layers across a list of files. Returns a verdict + every
|
|
123
|
+
* matching rule across every file. Empty findings = clean bundle.
|
|
124
|
+
*
|
|
125
|
+
* Files where `content` is missing skip the content-regex stage but
|
|
126
|
+
* still trip path rules.
|
|
127
|
+
*/
|
|
128
|
+
export function scanForSecrets(files) {
|
|
129
|
+
const findings = [];
|
|
130
|
+
for (const file of files) {
|
|
131
|
+
// Path-based rules
|
|
132
|
+
for (const { rule, test } of PATH_RULES) {
|
|
133
|
+
if (test(file.path)) {
|
|
134
|
+
findings.push({ path: file.path, rule });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Content-based rules — only on text. We could be smarter (e.g.
|
|
138
|
+
// skip files >1MB) but the worker already caps bundle size and
|
|
139
|
+
// skips known-binary extensions before getting here.
|
|
140
|
+
if (typeof file.content === 'string' && file.content.length > 0) {
|
|
141
|
+
// Split once for line numbers; the regex tests run against the
|
|
142
|
+
// full body so multiline shapes still match.
|
|
143
|
+
const lines = file.content.split('\n');
|
|
144
|
+
for (const { rule, regex } of CONTENT_RULES) {
|
|
145
|
+
// Reset stateful regexes — we don't use /g but be defensive.
|
|
146
|
+
const localRegex = new RegExp(regex.source, regex.flags.replace('g', ''));
|
|
147
|
+
for (let i = 0; i < lines.length; i++) {
|
|
148
|
+
if (localRegex.test(lines[i] ?? '')) {
|
|
149
|
+
findings.push({ path: file.path, rule, line: i + 1 });
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
status: findings.length > 0 ? 'rejected' : 'clean',
|
|
158
|
+
findings,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Whether a path likely points at a text-readable file (worth running
|
|
163
|
+
* content regexes against). Used by callers to filter the input list
|
|
164
|
+
* before they bother loading binary blobs.
|
|
165
|
+
*/
|
|
166
|
+
export function isLikelyTextPath(path) {
|
|
167
|
+
const ext = path.toLowerCase().split('.').pop() ?? '';
|
|
168
|
+
return (TEXT_EXTENSIONS.has(ext) ||
|
|
169
|
+
// Files without an extension that we'd still want to scan
|
|
170
|
+
path.endsWith('Dockerfile') ||
|
|
171
|
+
path.endsWith('Makefile') ||
|
|
172
|
+
/\.env(\.|$)/.test(path));
|
|
173
|
+
}
|
|
174
|
+
const TEXT_EXTENSIONS = new Set([
|
|
175
|
+
'md',
|
|
176
|
+
'mdx',
|
|
177
|
+
'txt',
|
|
178
|
+
'json',
|
|
179
|
+
'yaml',
|
|
180
|
+
'yml',
|
|
181
|
+
'toml',
|
|
182
|
+
'ini',
|
|
183
|
+
'cfg',
|
|
184
|
+
'conf',
|
|
185
|
+
'env',
|
|
186
|
+
'js',
|
|
187
|
+
'jsx',
|
|
188
|
+
'ts',
|
|
189
|
+
'tsx',
|
|
190
|
+
'mjs',
|
|
191
|
+
'cjs',
|
|
192
|
+
'py',
|
|
193
|
+
'rb',
|
|
194
|
+
'go',
|
|
195
|
+
'rs',
|
|
196
|
+
'sh',
|
|
197
|
+
'bash',
|
|
198
|
+
'zsh',
|
|
199
|
+
'fish',
|
|
200
|
+
'sql',
|
|
201
|
+
'graphql',
|
|
202
|
+
'gql',
|
|
203
|
+
'html',
|
|
204
|
+
'xml',
|
|
205
|
+
'svg',
|
|
206
|
+
'css',
|
|
207
|
+
'scss',
|
|
208
|
+
'less',
|
|
209
|
+
'lock',
|
|
210
|
+
]);
|
|
211
|
+
function basename(p) {
|
|
212
|
+
const i = p.lastIndexOf('/');
|
|
213
|
+
return i === -1 ? p : p.slice(i + 1);
|
|
214
|
+
}
|
package/dist/schemas/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,iBAAiB,CAAA"}
|
package/dist/schemas/index.js
CHANGED