@volter/teams 0.2.10 → 0.2.11
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/checkout.mjs +29 -0
- package/http-client.mjs +6 -1
- package/machine-door.mjs +22 -2
- package/package.json +9 -3
- package/redact/gitleaks-rules.json +1 -0
- package/redact.mjs +219 -0
- package/session-follow.mjs +12 -1
- package/viewer/session-view.css +39 -0
- package/viewer/session-view.mjs +28 -28
package/checkout.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// What a checkout on this machine is, read with git alone (no machine state): its repository identity, root, branch and
|
|
2
|
+
// whether it is a linked worktree. The machine daemon answers it for another machine; a process asking about a path on
|
|
3
|
+
// its own machine reads it itself, with the file access that process has (a launchd daemon may have none on a volume
|
|
4
|
+
// macOS guards, where the session that named the path does).
|
|
5
|
+
import { realpathSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
|
|
9
|
+
const git = (path, args, optional = false) => {
|
|
10
|
+
const r = spawnSync('git', ['-C', path, ...args], { encoding: 'utf8', timeout: 30_000 });
|
|
11
|
+
if (r.status !== 0) { if (optional) return null; throw new Error(`git ${args[0]} in ${path}: ${(r.stderr || r.error?.message || 'failed').trim()}`); }
|
|
12
|
+
return r.stdout.trim();
|
|
13
|
+
};
|
|
14
|
+
export function repositoryId(value) {
|
|
15
|
+
let s = String(value ?? '').trim().replace(/\.git\/?$/, '').replace(/\/$/, '');
|
|
16
|
+
if (/^[^/@]+@[^:]+:/.test(s)) s = s.replace(/^[^@]+@/, '').replace(':', '/');
|
|
17
|
+
else if (/^[a-z]+:\/\//i.test(s)) { const u = new URL(s); s = u.host + u.pathname; }
|
|
18
|
+
s = s.replace(/^github\.com\//i, '');
|
|
19
|
+
if (!/^[\w.-]+(?:\/[\w.-]+)+$/.test(s) || s.split('/').some(p => p === '.' || p === '..')) throw new Error('repository must be a git remote identity, for example volter-ai/editor; not a machine path');
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
export function inspectCheckout(path) {
|
|
23
|
+
const raw = git(path, ['rev-parse', '--path-format=absolute', '--show-toplevel', '--git-dir', '--git-common-dir']);
|
|
24
|
+
const [root, dir, common] = raw.split('\n').map(p => realpathSync(resolve(path, p)));
|
|
25
|
+
// The repository the checkout declares (remote.origin.url), not where a URL rewrite (insteadOf: a mirror, a proxy, a
|
|
26
|
+
// World's twin) sends its transport.
|
|
27
|
+
const remote = git(root, ['config', '--get', 'remote.origin.url'], true);
|
|
28
|
+
return { path: realpathSync(path), root, linked: dir !== common, repository: remote ? repositoryId(remote) : null, branch: git(root, ['branch', '--show-current']) || null };
|
|
29
|
+
}
|
package/http-client.mjs
CHANGED
|
@@ -13,12 +13,17 @@ export function callerName(env=globalThis.process?.env,argv=globalThis.process?.
|
|
|
13
13
|
return ([script,...words].join(' ')+(session?` in ${session}`:'')).replace(/[^\x20-\x7e]/g,'?').slice(0,120);
|
|
14
14
|
}
|
|
15
15
|
const CALLER=callerName();
|
|
16
|
+
// Every write to a Teams server is redacted (@volter/teams/redact), but a sign-in or credential exchange, whose body is
|
|
17
|
+
// the credential it hands over.
|
|
18
|
+
const SIGN_IN=/\/(?:login|apps\/token|invitations\/accept|credentials|me\/identities)(?:\/|$)/;
|
|
19
|
+
// Node only (it reads custody files); a browser sends what its person typed, and a bundler must not pull it in.
|
|
20
|
+
const { redactValue } = globalThis.process?.versions?.node ? await import(`./redact${'.mjs'}`) : { redactValue: (value) => value };
|
|
16
21
|
export class TeamsClient {
|
|
17
22
|
constructor({server,credential,fetch:fetchImpl=(...args)=>globalThis.fetch(...args)}){this.server=trimOrigin(server);this.credential=credential;this.fetch=fetchImpl;if(typeof fetchImpl!=='function')throw new TypeError('fetch is required');}
|
|
18
23
|
async request(method,path,{body,signal,idempotencyKey,ifMatch}={}) {
|
|
19
24
|
const key=idempotencyKey??(method==='GET'?null:randomKey());let delay=250,last;
|
|
20
25
|
for(let attempt=0;attempt<5;attempt++) { try {
|
|
21
|
-
const response=await this.fetch(this.server+path,{method,signal,redirect:'manual',headers:{Accept:'application/json',Authorization:`Bearer ${this.credential}`,...(CALLER?{'X-Supercode-Caller':CALLER}:{}),...(body!==undefined?{'Content-Type':'application/json'}:{}),...(key?{'Idempotency-Key':key}:{}),...(ifMatch!==undefined?{'If-Match':String(ifMatch)}:{})},body:body===undefined?undefined:JSON.stringify(body)});
|
|
26
|
+
const response=await this.fetch(this.server+path,{method,signal,redirect:'manual',headers:{Accept:'application/json',Authorization:`Bearer ${this.credential}`,...(CALLER?{'X-Supercode-Caller':CALLER}:{}),...(body!==undefined?{'Content-Type':'application/json'}:{}),...(key?{'Idempotency-Key':key}:{}),...(ifMatch!==undefined?{'If-Match':String(ifMatch)}:{})},body:body===undefined?undefined:JSON.stringify(SIGN_IN.test(path)?body:redactValue(body))});
|
|
22
27
|
if(response.type==='opaqueredirect'||(response.status>=300&&response.status<400))throw Object.assign(new Error('cross-origin redirects are refused'),{code:'invalid_request',status:400,retryable:false});
|
|
23
28
|
const value=response.status===204?null:await response.json();if(!response.ok)throw Object.assign(new Error(value?.error?.message??`HTTP ${response.status}`),value?.error,{status:response.status});return value;
|
|
24
29
|
} catch(error) { last=error;if(method!=='GET'||signal?.aborted||!(!error.status||error.retryable)||attempt===4)throw error;await retryWait(delay,signal);delay=Math.min(delay*2,5000); } }
|
package/machine-door.mjs
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
// Which door a machine verb goes through: without a machine name, this OS
|
|
2
2
|
// user's own daemon over its local socket; with one, the Teams server of the
|
|
3
3
|
// selected context, which forwards the channel to that enrolled machine.
|
|
4
|
-
import {
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { hostname } from 'node:os';
|
|
6
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
7
|
import { openLocalMachine, openRemoteMachine } from './client.mjs';
|
|
6
8
|
import { WorkspaceContexts, fromContext } from './context.mjs';
|
|
7
9
|
import { teamsHome, paths } from './home.mjs';
|
|
8
10
|
|
|
9
11
|
export async function openMachine({ machine = null, context = null, env = process.env, home = teamsHome(env) } = {}) {
|
|
10
12
|
if (!machine) return openLocalMachine(paths(home).socket);
|
|
13
|
+
// This machine named by its own name is its own daemon, reached over its socket: through the Teams server it waited
|
|
14
|
+
// on the org's machine list and a server channel, and a launch on this box failed whenever the server was rate
|
|
15
|
+
// limited or out of reach (two review resumes, 17:03Z and 17:34Z, retired as never issued).
|
|
16
|
+
if (!machine.startsWith('mach_') && normalMachineName(machine) === normalMachineName(hostname()) && existsSync(paths(home).socket)) return openLocalMachine(paths(home).socket);
|
|
11
17
|
const supercodeHome = env.SUPERCODE_HOME ?? resolve(home, '..');
|
|
12
18
|
const contexts = new WorkspaceContexts(supercodeHome);
|
|
13
19
|
const name = context ?? env.SUPERCODE_CONTEXT ?? contexts.read().current;
|
|
@@ -15,7 +21,14 @@ export async function openMachine({ machine = null, context = null, env = proces
|
|
|
15
21
|
const { client, context: selected } = fromContext({ supercodeHome, name, fetch: globalThis.fetch });
|
|
16
22
|
const credential = contexts.credential(selected.credential_ref);
|
|
17
23
|
let machineId = machine;
|
|
18
|
-
|
|
24
|
+
// A machine's name resolves to its id once per ten minutes, kept beside the contexts: every mail to another machine
|
|
25
|
+
// listed the org's machines on the shared credential's allowance (`teams mail <machine>` at 18 a minute).
|
|
26
|
+
const cacheFile = join(supercodeHome, 'cache', 'machine-ids.json');
|
|
27
|
+
const cacheKey = `${selected.origin}|${selected.team_id}|${normalMachineName(machine)}`;
|
|
28
|
+
let cache = {};
|
|
29
|
+
try { cache = JSON.parse(readFileSync(cacheFile, 'utf8')); } catch { /* none yet */ }
|
|
30
|
+
if (!machine.startsWith('mach_') && cache[cacheKey] && Date.now() - cache[cacheKey].at < 600_000) machineId = cache[cacheKey].id;
|
|
31
|
+
else if (!machine.startsWith('mach_')) {
|
|
19
32
|
const listed = (await client.team(selected.team_id).machines.list()).items;
|
|
20
33
|
let matches = listed.filter((item) => item.name === machine);
|
|
21
34
|
// A mail address names a machine by its short, lowercased host name
|
|
@@ -24,6 +37,13 @@ export async function openMachine({ machine = null, context = null, env = proces
|
|
|
24
37
|
if (!matches.length) matches = listed.filter((item) => normalMachineName(item.name) === normalMachineName(machine));
|
|
25
38
|
if (matches.length !== 1) throw Object.assign(new Error(matches.length ? `machine name ${machine} is ambiguous; use its id` : `machine ${machine} is not accessible`), { code: matches.length ? 'ambiguous_reference' : 'not_found' });
|
|
26
39
|
machineId = matches[0].id;
|
|
40
|
+
try {
|
|
41
|
+
mkdirSync(dirname(cacheFile), { recursive: true });
|
|
42
|
+
const fresh = Object.fromEntries(Object.entries(cache).filter(([, entry]) => Date.now() - entry.at < 600_000));
|
|
43
|
+
const temporary = `${cacheFile}.${process.pid}.tmp`;
|
|
44
|
+
writeFileSync(temporary, JSON.stringify({ ...fresh, [cacheKey]: { id: machineId, at: Date.now() } }));
|
|
45
|
+
renameSync(temporary, cacheFile);
|
|
46
|
+
} catch { /* an unwritable cache only costs the next lookup */ }
|
|
27
47
|
}
|
|
28
48
|
const channel = await openRemoteMachine({ origin: selected.origin, teamId: selected.team_id, machineId, token: credential.token });
|
|
29
49
|
channel.machineName = machine;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/teams",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Volter Teams, the neutral team plane: machine identity and daemon core, the local door and server link, admission from machine grants, product doors, and the Teams server's neutral routes (company RFC 0008).",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,11 @@
|
|
|
49
49
|
"./machine-door": "./machine-door.mjs",
|
|
50
50
|
"./sender-identity": "./sender-identity.mjs",
|
|
51
51
|
"./board-process-guard": "./board-process-guard.mjs",
|
|
52
|
-
"./server/agent-authority": "./server/agent-authority.mjs"
|
|
52
|
+
"./server/agent-authority": "./server/agent-authority.mjs",
|
|
53
|
+
"./redact": {
|
|
54
|
+
"default": "./redact.mjs"
|
|
55
|
+
},
|
|
56
|
+
"./checkout": "./checkout.mjs"
|
|
53
57
|
},
|
|
54
58
|
"files": [
|
|
55
59
|
"LICENSE-MIT",
|
|
@@ -60,7 +64,9 @@
|
|
|
60
64
|
"viewer",
|
|
61
65
|
"README.md",
|
|
62
66
|
"http-client.d.ts",
|
|
63
|
-
"identity-record.d.ts"
|
|
67
|
+
"identity-record.d.ts",
|
|
68
|
+
"redact.mjs",
|
|
69
|
+
"redact/"
|
|
64
70
|
],
|
|
65
71
|
"engines": {
|
|
66
72
|
"node": ">=22"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"source": "gitleaks v8.30.1 config/gitleaks.toml", "sha256": "e163e53b9e7e8a8511e77271e2b323ed057759542a6d988258afe3a1fa329caf", "rules": [{"id": "1password-secret-key", "regex": "\\bA3-[A-Z0-9]{6}-(?:(?:[A-Z0-9]{11})|(?:[A-Z0-9]{6}-[A-Z0-9]{5}))-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}\\b", "keywords": ["a3-"], "entropy": 3.8}, {"id": "1password-service-account-token", "regex": "ops_eyJ[a-zA-Z0-9+/]{250,}={0,3}", "keywords": ["ops_"], "entropy": 4}, {"id": "adafruit-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:adafruit)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9_-]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["adafruit"]}, {"id": "adobe-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:adobe)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["adobe"], "entropy": 2}, {"id": "adobe-client-secret", "regex": "\\b(p8e-(?i)[a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["p8e-"], "entropy": 2}, {"id": "age-secret-key", "regex": "AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}", "keywords": ["age-secret-key-1"]}, {"id": "airtable-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:airtable)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{17})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["airtable"]}, {"id": "airtable-personnal-access-token", "regex": "\\b(pat[[:alnum:]]{14}\\.[a-f0-9]{64})\\b", "keywords": ["airtable"]}, {"id": "algolia-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:algolia)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["algolia"]}, {"id": "alibaba-access-key-id", "regex": "\\b(LTAI(?i)[a-z0-9]{20})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["ltai"], "entropy": 2}, {"id": "alibaba-secret-key", "regex": "(?i)[\\w.-]{0,50}?(?:alibaba)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{30})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["alibaba"], "entropy": 2}, {"id": "anthropic-admin-api-key", "regex": "\\b(sk-ant-admin01-[a-zA-Z0-9_\\-]{93}AA)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sk-ant-admin01"]}, {"id": "anthropic-api-key", "regex": "\\b(sk-ant-api03-[a-zA-Z0-9_\\-]{93}AA)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sk-ant-api03"]}, {"id": "artifactory-api-key", "regex": "\\bAKCp[A-Za-z0-9]{69}\\b", "keywords": ["akcp"], "entropy": 4.5}, {"id": "artifactory-reference-token", "regex": "\\bcmVmd[A-Za-z0-9]{59}\\b", "keywords": ["cmvmd"], "entropy": 4.5}, {"id": "asana-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:asana)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["asana"]}, {"id": "asana-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:asana)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["asana"]}, {"id": "atlassian-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:(?-i:ATLASSIAN|[Aa]tlassian)|(?-i:CONFLUENCE|[Cc]onfluence)|(?-i:JIRA|[Jj]ira))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{20}[a-f0-9]{4})(?:[\\x60'\"\\s;]|\\\\[nr]|$)|\\b(ATATT3[A-Za-z0-9_\\-=]{186})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["atlassian", "confluence", "jira", "atatt3"], "entropy": 3.5}, {"id": "authress-service-client-access-key", "regex": "\\b((?:sc|ext|scauth|authress)_(?i)[a-z0-9]{5,30}\\.[a-z0-9]{4,6}\\.(?-i:acc)[_-][a-z0-9-]{10,32}\\.[a-z0-9+/_=-]{30,120})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sc_", "ext_", "scauth_", "authress_"], "entropy": 2}, {"id": "aws-access-token", "regex": "\\b((?:A3T[A-Z0-9]|AKIA|ASIA|ABIA|ACCA)[A-Z2-7]{16})\\b", "keywords": ["a3t", "akia", "asia", "abia", "acca"], "entropy": 3}, {"id": "aws-amazon-bedrock-api-key-long-lived", "regex": "\\b(ABSK[A-Za-z0-9+/]{109,269}={0,2})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["absk"], "entropy": 3}, {"id": "aws-amazon-bedrock-api-key-short-lived", "regex": "bedrock-api-key-YmVkcm9jay5hbWF6b25hd3MuY29t", "keywords": ["bedrock-api-key-"], "entropy": 3}, {"id": "azure-ad-client-secret", "regex": "(?:^|[\\\\'\"\\x60\\s>=:(,)])([a-zA-Z0-9_~.]{3}\\dQ~[a-zA-Z0-9_~.-]{31,34})(?:$|[\\\\'\"\\x60\\s<),])", "keywords": ["q~"], "entropy": 3}, {"id": "beamer-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:beamer)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(b_[a-z0-9=_\\-]{44})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["beamer"]}, {"id": "bitbucket-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:bitbucket)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["bitbucket"]}, {"id": "bitbucket-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:bitbucket)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["bitbucket"]}, {"id": "bittrex-access-key", "regex": "(?i)[\\w.-]{0,50}?(?:bittrex)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["bittrex"]}, {"id": "bittrex-secret-key", "regex": "(?i)[\\w.-]{0,50}?(?:bittrex)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["bittrex"]}, {"id": "cisco-meraki-api-key", "regex": "[\\w.-]{0,50}?(?i:[\\w.-]{0,50}?(?:(?-i:[Mm]eraki|MERAKI))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3})(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["meraki"], "entropy": 3}, {"id": "clickhouse-cloud-api-secret-key", "regex": "\\b(4b1d[A-Za-z0-9]{38})\\b", "keywords": ["4b1d"], "entropy": 3}, {"id": "clojars-api-token", "regex": "(?i)CLOJARS_[a-z0-9]{60}", "keywords": ["clojars_"], "entropy": 2}, {"id": "cloudflare-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:cloudflare)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9_-]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["cloudflare"], "entropy": 2}, {"id": "cloudflare-global-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:cloudflare)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{37})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["cloudflare"], "entropy": 2}, {"id": "cloudflare-origin-ca-key", "regex": "\\b(v1\\.0-[a-f0-9]{24}-[a-f0-9]{146})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["cloudflare", "v1.0-"], "entropy": 2}, {"id": "codecov-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:codecov)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["codecov"]}, {"id": "cohere-api-token", "regex": "[\\w.-]{0,50}?(?i:[\\w.-]{0,50}?(?:cohere|CO_API_KEY)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3})(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-zA-Z0-9]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["cohere", "co_api_key"], "entropy": 4}, {"id": "coinbase-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:coinbase)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9_-]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["coinbase"]}, {"id": "confluent-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:confluent)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["confluent"]}, {"id": "confluent-secret-key", "regex": "(?i)[\\w.-]{0,50}?(?:confluent)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["confluent"]}, {"id": "contentful-delivery-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:contentful)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{43})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["contentful"]}, {"id": "curl-auth-header", "regex": "\\bcurl\\b(?:.*?|.*?(?:[\\r\\n]{1,2}.*?){1,5})[ \\t\\n\\r](?:-H|--header)(?:=|[ \\t]{0,5})(?:\"(?i)(?:Authorization:[ \\t]{0,5}(?:Basic[ \\t]([a-z0-9+/]{8,}={0,3})|(?:Bearer|(?:Api-)?Token)[ \\t]([\\w=~@.+/-]{8,})|([\\w=~@.+/-]{8,}))|(?:(?:X-(?:[a-z]+-)?)?(?:Api-?)?(?:Key|Token)):[ \\t]{0,5}([\\w=~@.+/-]{8,}))\"|'(?i)(?:Authorization:[ \\t]{0,5}(?:Basic[ \\t]([a-z0-9+/]{8,}={0,3})|(?:Bearer|(?:Api-)?Token)[ \\t]([\\w=~@.+/-]{8,})|([\\w=~@.+/-]{8,}))|(?:(?:X-(?:[a-z]+-)?)?(?:Api-?)?(?:Key|Token)):[ \\t]{0,5}([\\w=~@.+/-]{8,}))')(?:\\B|\\s|\\z)", "keywords": ["curl"], "entropy": 2.75}, {"id": "curl-auth-user", "regex": "\\bcurl\\b(?:.*|.*(?:[\\r\\n]{1,2}.*){1,5})[ \\t\\n\\r](?:-u|--user)(?:=|[ \\t]{0,5})(\"(:[^\"]{3,}|[^:\"]{3,}:|[^:\"]{3,}:[^\"]{3,})\"|'([^:']{3,}:[^']{3,})'|((?:\"[^\"]{3,}\"|'[^']{3,}'|[\\w$@.-]+):(?:\"[^\"]{3,}\"|'[^']{3,}'|[\\w${}@.-]+)))(?:\\s|\\z)", "keywords": ["curl"], "entropy": 2}, {"id": "databricks-api-token", "regex": "\\b(dapi[a-f0-9]{32}(?:-\\d)?)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dapi"], "entropy": 3}, {"id": "datadog-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:datadog)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["datadog"]}, {"id": "defined-networking-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:dnkey)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(dnkey-[a-z0-9=_\\-]{26}-[a-z0-9=_\\-]{52})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dnkey"]}, {"id": "digitalocean-access-token", "regex": "\\b(doo_v1_[a-f0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["doo_v1_"], "entropy": 3}, {"id": "digitalocean-pat", "regex": "\\b(dop_v1_[a-f0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dop_v1_"], "entropy": 3}, {"id": "digitalocean-refresh-token", "regex": "(?i)\\b(dor_v1_[a-f0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dor_v1_"]}, {"id": "discord-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:discord)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["discord"]}, {"id": "discord-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:discord)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9]{18})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["discord"], "entropy": 2}, {"id": "discord-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:discord)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["discord"], "entropy": 2}, {"id": "doppler-api-token", "regex": "dp\\.pt\\.(?i)[a-z0-9]{43}", "keywords": ["dp.pt."], "entropy": 2}, {"id": "droneci-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:droneci)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["droneci"]}, {"id": "dropbox-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:dropbox)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{15})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dropbox"]}, {"id": "dropbox-long-lived-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:dropbox)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{11}(AAAAAAAAAA)[a-z0-9\\-_=]{43})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dropbox"]}, {"id": "dropbox-short-lived-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:dropbox)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(sl\\.[a-z0-9\\-=_]{135})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["dropbox"]}, {"id": "duffel-api-token", "regex": "duffel_(?:test|live)_(?i)[a-z0-9_\\-=]{43}", "keywords": ["duffel_"], "entropy": 2}, {"id": "dynatrace-api-token", "regex": "dt0c01\\.(?i)[a-z0-9]{24}\\.[a-z0-9]{64}", "keywords": ["dt0c01."], "entropy": 4}, {"id": "easypost-api-token", "regex": "\\bEZAK(?i)[a-z0-9]{54}\\b", "keywords": ["ezak"], "entropy": 2}, {"id": "easypost-test-api-token", "regex": "\\bEZTK(?i)[a-z0-9]{54}\\b", "keywords": ["eztk"], "entropy": 2}, {"id": "etsy-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:(?-i:ETSY|[Ee]tsy))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{24})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["etsy"], "entropy": 3}, {"id": "facebook-access-token", "regex": "(?i)\\b(\\d{15,16}(\\||%)[0-9a-z\\-_]{27,40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["facebook"], "entropy": 3}, {"id": "facebook-page-access-token", "regex": "\\b(EAA[MC](?i)[a-z0-9]{100,})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["eaam", "eaac"], "entropy": 4}, {"id": "facebook-secret", "regex": "(?i)[\\w.-]{0,50}?(?:facebook)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["facebook"], "entropy": 3}, {"id": "fastly-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:fastly)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["fastly"]}, {"id": "finicity-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:finicity)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["finicity"]}, {"id": "finicity-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:finicity)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{20})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["finicity"]}, {"id": "finnhub-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:finnhub)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{20})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["finnhub"]}, {"id": "flickr-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:flickr)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["flickr"]}, {"id": "flutterwave-encryption-key", "regex": "FLWSECK_TEST-(?i)[a-h0-9]{12}", "keywords": ["flwseck_test"], "entropy": 2}, {"id": "flutterwave-public-key", "regex": "FLWPUBK_TEST-(?i)[a-h0-9]{32}-X", "keywords": ["flwpubk_test"], "entropy": 2}, {"id": "flutterwave-secret-key", "regex": "FLWSECK_TEST-(?i)[a-h0-9]{32}-X", "keywords": ["flwseck_test"], "entropy": 2}, {"id": "flyio-access-token", "regex": "\\b((?:fo1_[\\w-]{43}|fm1[ar]_[a-zA-Z0-9+\\/]{100,}={0,3}|fm2_[a-zA-Z0-9+\\/]{100,}={0,3}))(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["fo1_", "fm1", "fm2_"], "entropy": 4}, {"id": "frameio-api-token", "regex": "fio-u-(?i)[a-z0-9\\-_=]{64}", "keywords": ["fio-u-"]}, {"id": "freemius-secret-key", "regex": "(?i)[\"']secret_key[\"']\\s*=>\\s*[\"'](sk_[\\S]{29})[\"']", "keywords": ["secret_key"]}, {"id": "freshbooks-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:freshbooks)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["freshbooks"]}, {"id": "gcp-api-key", "regex": "\\b(AIza[\\w-]{35})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["aiza"], "entropy": 4}, {"id": "generic-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:access|auth|(?-i:[Aa]pi|API)|credential|creds|key|passw(?:or)?d|secret|token)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([\\w.=-]{10,150}|[a-z0-9][a-z0-9+/]{11,}={0,3})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["access", "api", "auth", "key", "credential", "creds", "passwd", "password", "secret", "token"], "entropy": 3.5}, {"id": "github-app-token", "regex": "(?:ghu|ghs)_[0-9a-zA-Z]{36}", "keywords": ["ghu_", "ghs_"], "entropy": 3}, {"id": "github-fine-grained-pat", "regex": "github_pat_\\w{82}", "keywords": ["github_pat_"], "entropy": 3}, {"id": "github-oauth", "regex": "gho_[0-9a-zA-Z]{36}", "keywords": ["gho_"], "entropy": 3}, {"id": "github-pat", "regex": "ghp_[0-9a-zA-Z]{36}", "keywords": ["ghp_"], "entropy": 3}, {"id": "github-refresh-token", "regex": "ghr_[0-9a-zA-Z]{36}", "keywords": ["ghr_"], "entropy": 3}, {"id": "gitlab-cicd-job-token", "regex": "glcbt-[0-9a-zA-Z]{1,5}_[0-9a-zA-Z_-]{20}", "keywords": ["glcbt-"], "entropy": 3}, {"id": "gitlab-deploy-token", "regex": "gldt-[0-9a-zA-Z_\\-]{20}", "keywords": ["gldt-"], "entropy": 3}, {"id": "gitlab-feature-flag-client-token", "regex": "glffct-[0-9a-zA-Z_\\-]{20}", "keywords": ["glffct-"], "entropy": 3}, {"id": "gitlab-feed-token", "regex": "glft-[0-9a-zA-Z_\\-]{20}", "keywords": ["glft-"], "entropy": 3}, {"id": "gitlab-incoming-mail-token", "regex": "glimt-[0-9a-zA-Z_\\-]{25}", "keywords": ["glimt-"], "entropy": 3}, {"id": "gitlab-kubernetes-agent-token", "regex": "glagent-[0-9a-zA-Z_\\-]{50}", "keywords": ["glagent-"], "entropy": 3}, {"id": "gitlab-oauth-app-secret", "regex": "gloas-[0-9a-zA-Z_\\-]{64}", "keywords": ["gloas-"], "entropy": 3}, {"id": "gitlab-pat", "regex": "glpat-[\\w-]{20}", "keywords": ["glpat-"], "entropy": 3}, {"id": "gitlab-pat-routable", "regex": "\\bglpat-[0-9a-zA-Z_-]{27,300}\\.[0-9a-z]{2}[0-9a-z]{7}\\b", "keywords": ["glpat-"], "entropy": 4}, {"id": "gitlab-ptt", "regex": "glptt-[0-9a-f]{40}", "keywords": ["glptt-"], "entropy": 3}, {"id": "gitlab-rrt", "regex": "GR1348941[\\w-]{20}", "keywords": ["gr1348941"], "entropy": 3}, {"id": "gitlab-runner-authentication-token", "regex": "glrt-[0-9a-zA-Z_\\-]{20}", "keywords": ["glrt-"], "entropy": 3}, {"id": "gitlab-runner-authentication-token-routable", "regex": "\\bglrt-t\\d_[0-9a-zA-Z_\\-]{27,300}\\.[0-9a-z]{2}[0-9a-z]{7}\\b", "keywords": ["glrt-"], "entropy": 4}, {"id": "gitlab-scim-token", "regex": "glsoat-[0-9a-zA-Z_\\-]{20}", "keywords": ["glsoat-"], "entropy": 3}, {"id": "gitlab-session-cookie", "regex": "_gitlab_session=[0-9a-z]{32}", "keywords": ["_gitlab_session="], "entropy": 3}, {"id": "gitter-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:gitter)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9_-]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["gitter"]}, {"id": "gocardless-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:gocardless)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(live_(?i)[a-z0-9\\-_=]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["live_", "gocardless"]}, {"id": "grafana-api-key", "regex": "(?i)\\b(eyJrIjoi[A-Za-z0-9]{70,400}={0,3})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["eyjrijoi"], "entropy": 3}, {"id": "grafana-cloud-api-token", "regex": "(?i)\\b(glc_[A-Za-z0-9+/]{32,400}={0,3})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["glc_"], "entropy": 3}, {"id": "grafana-service-account-token", "regex": "(?i)\\b(glsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["glsa_"], "entropy": 3}, {"id": "harness-api-key", "regex": "(?:pat|sat)\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9]{24}\\.[a-zA-Z0-9]{20}", "keywords": ["pat.", "sat."]}, {"id": "hashicorp-tf-api-token", "regex": "(?i)[a-z0-9]{14}\\.(?-i:atlasv1)\\.[a-z0-9\\-_=]{60,70}", "keywords": ["atlasv1"], "entropy": 3.5}, {"id": "hashicorp-tf-password", "regex": "(?i)[\\w.-]{0,50}?(?:administrator_login_password|password)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(\"[a-z0-9=_\\-]{8,20}\")(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["administrator_login_password", "password"], "entropy": 2}, {"id": "heroku-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:heroku)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["heroku"]}, {"id": "heroku-api-key-v2", "regex": "\\b((HRKU-AA[0-9a-zA-Z_-]{58}))(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["hrku-aa"], "entropy": 4}, {"id": "hubspot-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:hubspot)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["hubspot"]}, {"id": "huggingface-access-token", "regex": "\\b(hf_(?i:[a-z]{34}))(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["hf_"], "entropy": 2}, {"id": "huggingface-organization-api-token", "regex": "\\b(api_org_(?i:[a-z]{34}))(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["api_org_"], "entropy": 2}, {"id": "infracost-api-token", "regex": "\\b(ico-[a-zA-Z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["ico-"], "entropy": 3}, {"id": "intercom-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:intercom)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{60})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["intercom"]}, {"id": "intra42-client-secret", "regex": "\\b(s-s4t2(?:ud|af)-(?i)[abcdef0123456789]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["intra", "s-s4t2ud-", "s-s4t2af-"], "entropy": 3}, {"id": "jfrog-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:jfrog|artifactory|bintray|xray)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{73})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["jfrog", "artifactory", "bintray", "xray"]}, {"id": "jfrog-identity-token", "regex": "(?i)[\\w.-]{0,50}?(?:jfrog|artifactory|bintray|xray)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["jfrog", "artifactory", "bintray", "xray"]}, {"id": "jwt", "regex": "\\b(ey[a-zA-Z0-9]{17,}\\.ey[a-zA-Z0-9\\/\\\\_-]{17,}\\.(?:[a-zA-Z0-9\\/\\\\_-]{10,}={0,2})?)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["ey"], "entropy": 3}, {"id": "jwt-base64", "regex": "\\bZXlK(?:(?P<alg>aGJHY2lPaU)|(?P<apu>aGNIVWlPaU)|(?P<apv>aGNIWWlPaU)|(?P<aud>aGRXUWlPaU)|(?P<b64>aU5qUWlP)|(?P<crit>amNtbDBJanBi)|(?P<cty>amRIa2lPaU)|(?P<epk>bGNHc2lPbn)|(?P<enc>bGJtTWlPaU)|(?P<jku>cWEzVWlPaU)|(?P<jwk>cWQyc2lPb)|(?P<iss>cGMzTWlPaU)|(?P<iv>cGRpSTZJ)|(?P<kid>cmFXUWlP)|(?P<key_ops>clpYbGZiM0J6SWpwY)|(?P<kty>cmRIa2lPaUp)|(?P<nonce>dWIyNWpaU0k2)|(?P<p2c>d01tTWlP)|(?P<p2s>d01uTWlPaU)|(?P<ppt>d2NIUWlPaU)|(?P<sub>emRXSWlPaU)|(?P<svt>emRuUWlP)|(?P<tag>MFlXY2lPaU)|(?P<typ>MGVYQWlPaUp)|(?P<url>MWNtd2l)|(?P<use>MWMyVWlPaUp)|(?P<ver>MlpYSWlPaU)|(?P<version>MlpYSnphVzl1SWpv)|(?P<x>NElqb2)|(?P<x5c>NE5XTWlP)|(?P<x5t>NE5YUWlPaU)|(?P<x5ts256>NE5YUWpVekkxTmlJNkl)|(?P<x5u>NE5YVWlPaU)|(?P<zip>NmFYQWlPaU))[a-zA-Z0-9\\/\\\\_+\\-\\r\\n]{40,}={0,2}", "keywords": ["zxlk"], "entropy": 2}, {"id": "kraken-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:kraken)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9\\/=_\\+\\-]{80,90})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["kraken"]}, {"id": "kubernetes-secret-yaml", "regex": "(?i)(?:\\bkind:[ \\t]*[\"']?\\bsecret\\b[\"']?(?s:.){0,200}?\\bdata:(?s:.){0,100}?\\s+([\\w.-]+:(?:[ \\t]*(?:\\||>[-+]?)\\s+)?[ \\t]*(?:[\"']?[a-z0-9+/]{10,}={0,3}[\"']?|\\{\\{[ \\t\\w\"|$:=,.-]+}}|\"\"|''))|\\bdata:(?s:.){0,100}?\\s+([\\w.-]+:(?:[ \\t]*(?:\\||>[-+]?)\\s+)?[ \\t]*(?:[\"']?[a-z0-9+/]{10,}={0,3}[\"']?|\\{\\{[ \\t\\w\"|$:=,.-]+}}|\"\"|''))(?s:.){0,200}?\\bkind:[ \\t]*[\"']?\\bsecret\\b[\"']?)", "keywords": ["secret"]}, {"id": "kucoin-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:kucoin)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{24})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["kucoin"]}, {"id": "kucoin-secret-key", "regex": "(?i)[\\w.-]{0,50}?(?:kucoin)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["kucoin"]}, {"id": "launchdarkly-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:launchdarkly)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["launchdarkly"]}, {"id": "linear-api-key", "regex": "lin_api_(?i)[a-z0-9]{40}", "keywords": ["lin_api_"], "entropy": 2}, {"id": "linear-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:linear)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["linear"], "entropy": 2}, {"id": "linkedin-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:linked[_-]?in)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{14})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["linkedin", "linked_in", "linked-in"], "entropy": 2}, {"id": "linkedin-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:linked[_-]?in)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["linkedin", "linked_in", "linked-in"], "entropy": 2}, {"id": "lob-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:lob)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}((live|test)_[a-f0-9]{35})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["test_", "live_"]}, {"id": "lob-pub-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:lob)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}((test|live)_pub_[a-f0-9]{31})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["test_pub", "live_pub", "_pub"]}, {"id": "looker-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:looker)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{20})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["looker"]}, {"id": "looker-client-secret", "regex": "(?i)[\\w.-]{0,50}?(?:looker)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{24})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["looker"]}, {"id": "mailchimp-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:MailchimpSDK.initialize|mailchimp)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{32}-us\\d\\d)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["mailchimp"]}, {"id": "mailgun-private-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:mailgun)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(key-[a-f0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["mailgun"]}, {"id": "mailgun-pub-key", "regex": "(?i)[\\w.-]{0,50}?(?:mailgun)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(pubkey-[a-f0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["mailgun"]}, {"id": "mailgun-signing-key", "regex": "(?i)[\\w.-]{0,50}?(?:mailgun)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["mailgun"]}, {"id": "mapbox-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:mapbox)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(pk\\.[a-z0-9]{60}\\.[a-z0-9]{22})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["mapbox"]}, {"id": "mattermost-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:mattermost)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{26})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["mattermost"]}, {"id": "maxmind-license-key", "regex": "\\b([A-Za-z0-9]{6}_[A-Za-z0-9]{29}_mmk)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["_mmk"], "entropy": 4}, {"id": "messagebird-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:message[_-]?bird)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{25})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["messagebird", "message-bird", "message_bird"]}, {"id": "messagebird-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:message[_-]?bird)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["messagebird", "message-bird", "message_bird"]}, {"id": "microsoft-teams-webhook", "regex": "https://[a-z0-9]+\\.webhook\\.office\\.com/webhookb2/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}@[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}/IncomingWebhook/[a-z0-9]{32}/[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}", "keywords": ["webhook.office.com", "webhookb2", "incomingwebhook"]}, {"id": "netlify-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:netlify)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{40,46})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["netlify"]}, {"id": "new-relic-browser-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(NRJS-[a-f0-9]{19})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["nrjs-"]}, {"id": "new-relic-insert-key", "regex": "(?i)[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(NRII-[a-z0-9-]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["nrii-"]}, {"id": "new-relic-user-api-id", "regex": "(?i)[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["new-relic", "newrelic", "new_relic"]}, {"id": "new-relic-user-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:new-relic|newrelic|new_relic)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(NRAK-[a-z0-9]{27})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["nrak"]}, {"id": "notion-api-token", "regex": "\\b(ntn_[0-9]{11}[A-Za-z0-9]{32}[A-Za-z0-9]{3})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["ntn_"], "entropy": 4}, {"id": "npm-access-token", "regex": "(?i)\\b(npm_[a-z0-9]{36})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["npm_"], "entropy": 2}, {"id": "nuget-config-password", "regex": "(?i)<add key=\\\"(?:(?:ClearText)?Password)\\\"\\s*value=\\\"(.{8,})\\\"\\s*/>", "keywords": ["<add key="], "entropy": 1}, {"id": "nytimes-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:nytimes|new-york-times,|newyorktimes)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9=_\\-]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["nytimes", "new-york-times", "newyorktimes"]}, {"id": "octopus-deploy-api-key", "regex": "\\b(API-[A-Z0-9]{26})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["api-"], "entropy": 3}, {"id": "okta-access-token", "regex": "[\\w.-]{0,50}?(?i:[\\w.-]{0,50}?(?:(?-i:[Oo]kta|OKTA))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3})(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(00[\\w=\\-]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["okta"], "entropy": 4}, {"id": "openai-api-key", "regex": "\\b(sk-(?:proj|svcacct|admin)-(?:[A-Za-z0-9_-]{74}|[A-Za-z0-9_-]{58})T3BlbkFJ(?:[A-Za-z0-9_-]{74}|[A-Za-z0-9_-]{58})\\b|sk-[a-zA-Z0-9]{20}T3BlbkFJ[a-zA-Z0-9]{20})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["t3blbkfj"], "entropy": 3}, {"id": "openshift-user-token", "regex": "\\b(sha256~[\\w-]{43})(?:[^\\w-]|\\z)", "keywords": ["sha256~"], "entropy": 3.5}, {"id": "perplexity-api-key", "regex": "\\b(pplx-[a-zA-Z0-9]{48})(?:[\\x60'\"\\s;]|\\\\[nr]|$|\\b)", "keywords": ["pplx-"], "entropy": 4}, {"id": "plaid-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:plaid)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(access-(?:sandbox|development|production)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["plaid"]}, {"id": "plaid-client-id", "regex": "(?i)[\\w.-]{0,50}?(?:plaid)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{24})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["plaid"], "entropy": 3.5}, {"id": "plaid-secret-key", "regex": "(?i)[\\w.-]{0,50}?(?:plaid)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{30})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["plaid"], "entropy": 3.5}, {"id": "planetscale-api-token", "regex": "\\b(pscale_tkn_(?i)[\\w=\\.-]{32,64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["pscale_tkn_"], "entropy": 3}, {"id": "planetscale-oauth-token", "regex": "\\b(pscale_oauth_[\\w=\\.-]{32,64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["pscale_oauth_"], "entropy": 3}, {"id": "planetscale-password", "regex": "(?i)\\b(pscale_pw_(?i)[\\w=\\.-]{32,64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["pscale_pw_"], "entropy": 3}, {"id": "postman-api-token", "regex": "\\b(PMAK-(?i)[a-f0-9]{24}\\-[a-f0-9]{34})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["pmak-"], "entropy": 3}, {"id": "prefect-api-token", "regex": "\\b(pnu_[a-zA-Z0-9]{36})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["pnu_"], "entropy": 2}, {"id": "private-key", "regex": "(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY(?: BLOCK)?-----[\\s\\S-]{64,}?KEY(?: BLOCK)?-----", "keywords": ["-----begin"]}, {"id": "privateai-api-token", "regex": "[\\w.-]{0,50}?(?i:[\\w.-]{0,50}?(?:private[_-]?ai)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3})(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{32})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["privateai", "private_ai", "private-ai"], "entropy": 3}, {"id": "pulumi-api-token", "regex": "\\b(pul-[a-f0-9]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["pul-"], "entropy": 2}, {"id": "pypi-upload-token", "regex": "pypi-AgEIcHlwaS5vcmc[\\w-]{50,1000}", "keywords": ["pypi-ageichlwas5vcmc"], "entropy": 3}, {"id": "rapidapi-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:rapidapi)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9_-]{50})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["rapidapi"]}, {"id": "readme-api-token", "regex": "\\b(rdme_[a-z0-9]{70})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["rdme_"], "entropy": 2}, {"id": "rubygems-api-token", "regex": "\\b(rubygems_[a-f0-9]{48})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["rubygems_"], "entropy": 2}, {"id": "scalingo-api-token", "regex": "\\b(tk-us-[\\w-]{48})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["tk-us-"], "entropy": 2}, {"id": "sendbird-access-id", "regex": "(?i)[\\w.-]{0,50}?(?:sendbird)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sendbird"]}, {"id": "sendbird-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:sendbird)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sendbird"]}, {"id": "sendgrid-api-token", "regex": "\\b(SG\\.(?i)[a-z0-9=_\\-\\.]{66})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sg."], "entropy": 2}, {"id": "sendinblue-api-token", "regex": "\\b(xkeysib-[a-f0-9]{64}\\-(?i)[a-z0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["xkeysib-"], "entropy": 2}, {"id": "sentry-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:sentry)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sentry"], "entropy": 3}, {"id": "sentry-org-token", "regex": "\\bsntrys_eyJpYXQiO[a-zA-Z0-9+/]{10,200}(?:LCJyZWdpb25fdXJs|InJlZ2lvbl91cmwi|cmVnaW9uX3VybCI6)[a-zA-Z0-9+/]{10,200}={0,2}_[a-zA-Z0-9+/]{43}(?:[^a-zA-Z0-9+/]|\\z)", "keywords": ["sntrys_eyjpyxqio"], "entropy": 4.5}, {"id": "sentry-user-token", "regex": "\\b(sntryu_[a-f0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sntryu_"], "entropy": 3.5}, {"id": "settlemint-application-access-token", "regex": "\\b(sm_aat_[a-zA-Z0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sm_aat"], "entropy": 3}, {"id": "settlemint-personal-access-token", "regex": "\\b(sm_pat_[a-zA-Z0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sm_pat"], "entropy": 3}, {"id": "settlemint-service-access-token", "regex": "\\b(sm_sat_[a-zA-Z0-9]{16})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sm_sat"], "entropy": 3}, {"id": "shippo-api-token", "regex": "\\b(shippo_(?:live|test)_[a-fA-F0-9]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["shippo_"], "entropy": 2}, {"id": "shopify-access-token", "regex": "shpat_[a-fA-F0-9]{32}", "keywords": ["shpat_"], "entropy": 2}, {"id": "shopify-custom-access-token", "regex": "shpca_[a-fA-F0-9]{32}", "keywords": ["shpca_"], "entropy": 2}, {"id": "shopify-private-app-access-token", "regex": "shppa_[a-fA-F0-9]{32}", "keywords": ["shppa_"], "entropy": 2}, {"id": "shopify-shared-secret", "regex": "shpss_[a-fA-F0-9]{32}", "keywords": ["shpss_"], "entropy": 2}, {"id": "sidekiq-secret", "regex": "(?i)[\\w.-]{0,50}?(?:BUNDLE_ENTERPRISE__CONTRIBSYS__COM|BUNDLE_GEMS__CONTRIBSYS__COM)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-f0-9]{8}:[a-f0-9]{8})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["bundle_enterprise__contribsys__com", "bundle_gems__contribsys__com"]}, {"id": "sidekiq-sensitive-url", "regex": "(?i)\\bhttps?://([a-f0-9]{8}:[a-f0-9]{8})@(?:gems.contribsys.com|enterprise.contribsys.com)(?:[\\/|\\#|\\?|:]|$)", "keywords": ["gems.contribsys.com", "enterprise.contribsys.com"]}, {"id": "slack-app-token", "regex": "(?i)xapp-\\d-[A-Z0-9]+-\\d+-[a-z0-9]+", "keywords": ["xapp"], "entropy": 2}, {"id": "slack-bot-token", "regex": "xoxb-[0-9]{10,13}-[0-9]{10,13}[a-zA-Z0-9-]*", "keywords": ["xoxb"], "entropy": 3}, {"id": "slack-config-access-token", "regex": "(?i)xoxe.xox[bp]-\\d-[A-Z0-9]{163,166}", "keywords": ["xoxe.xoxb-", "xoxe.xoxp-"], "entropy": 2}, {"id": "slack-config-refresh-token", "regex": "(?i)xoxe-\\d-[A-Z0-9]{146}", "keywords": ["xoxe-"], "entropy": 2}, {"id": "slack-legacy-bot-token", "regex": "xoxb-[0-9]{8,14}-[a-zA-Z0-9]{18,26}", "keywords": ["xoxb"], "entropy": 2}, {"id": "slack-legacy-token", "regex": "xox[os]-\\d+-\\d+-\\d+-[a-fA-F\\d]+", "keywords": ["xoxo", "xoxs"], "entropy": 2}, {"id": "slack-legacy-workspace-token", "regex": "xox[ar]-(?:\\d-)?[0-9a-zA-Z]{8,48}", "keywords": ["xoxa", "xoxr"], "entropy": 2}, {"id": "slack-user-token", "regex": "xox[pe](?:-[0-9]{10,13}){3}-[a-zA-Z0-9-]{28,34}", "keywords": ["xoxp-", "xoxe-"], "entropy": 2}, {"id": "slack-webhook-url", "regex": "(?:https?://)?hooks.slack.com/(?:services|workflows|triggers)/[A-Za-z0-9+/]{43,56}", "keywords": ["hooks.slack.com"]}, {"id": "snyk-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:snyk[_.-]?(?:(?:api|oauth)[_.-]?)?(?:key|token))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["snyk"]}, {"id": "sonar-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:sonar[_.-]?(login|token))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}((?:squ_|sqp_|sqa_)?[a-z0-9=_\\-]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "secretGroup": 2, "keywords": ["sonar"]}, {"id": "sourcegraph-access-token", "regex": "(?i)\\b(\\b(sgp_(?:[a-fA-F0-9]{16}|local)_[a-fA-F0-9]{40}|sgp_[a-fA-F0-9]{40}|[a-fA-F0-9]{40})\\b)(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sgp_", "sourcegraph"], "entropy": 3}, {"id": "square-access-token", "regex": "\\b((?:EAAA|sq0atp-)[\\w-]{22,60})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sq0atp-", "eaaa"], "entropy": 2}, {"id": "squarespace-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:squarespace)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["squarespace"]}, {"id": "stripe-access-token", "regex": "\\b((?:sk|rk)_(?:test|live|prod)_[a-zA-Z0-9]{10,99})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sk_test", "sk_live", "sk_prod", "rk_test", "rk_live", "rk_prod"], "entropy": 2}, {"id": "sumologic-access-id", "regex": "[\\w.-]{0,50}?(?i:[\\w.-]{0,50}?(?:(?-i:[Ss]umo|SUMO))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3})(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(su[a-zA-Z0-9]{12})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sumo"], "entropy": 3}, {"id": "sumologic-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:(?-i:[Ss]umo|SUMO))(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{64})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["sumo"], "entropy": 3}, {"id": "telegram-bot-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:telegr)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9]{5,16}:(?-i:A)[a-z0-9_\\-]{34})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["telegr"]}, {"id": "travisci-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:travis)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{22})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["travis"]}, {"id": "twilio-api-key", "regex": "SK[0-9a-fA-F]{32}", "keywords": ["sk"], "entropy": 3}, {"id": "twitch-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:twitch)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{30})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["twitch"]}, {"id": "twitter-access-secret", "regex": "(?i)[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{45})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["twitter"]}, {"id": "twitter-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([0-9]{15,25}-[a-zA-Z0-9]{20,40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["twitter"]}, {"id": "twitter-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{25})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["twitter"]}, {"id": "twitter-api-secret", "regex": "(?i)[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{50})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["twitter"]}, {"id": "twitter-bearer-token", "regex": "(?i)[\\w.-]{0,50}?(?:twitter)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(A{22}[a-zA-Z0-9%]{80,100})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["twitter"]}, {"id": "typeform-api-token", "regex": "(?i)[\\w.-]{0,50}?(?:typeform)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(tfp_[a-z0-9\\-_\\.=]{59})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["tfp_"]}, {"id": "vault-batch-token", "regex": "\\b(hvb\\.[\\w-]{138,300})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["hvb."], "entropy": 4}, {"id": "vault-service-token", "regex": "\\b((?:hvs\\.[\\w-]{90,120}|s\\.(?i:[a-z0-9]{24})))(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["hvs.", "s."], "entropy": 3.5}, {"id": "yandex-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:yandex)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(t1\\.[A-Z0-9a-z_-]+[=]{0,2}\\.[A-Z0-9a-z_-]{86}[=]{0,2})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["yandex"]}, {"id": "yandex-api-key", "regex": "(?i)[\\w.-]{0,50}?(?:yandex)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(AQVN[A-Za-z0-9_\\-]{35,38})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["yandex"]}, {"id": "yandex-aws-access-token", "regex": "(?i)[\\w.-]{0,50}?(?:yandex)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}(YC[a-zA-Z0-9_\\-]{38})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["yandex"]}, {"id": "zendesk-secret-key", "regex": "(?i)[\\w.-]{0,50}?(?:zendesk)(?:[ \\t\\w.-]{0,20})[\\s'\"]{0,3}(?:=|>|:{1,3}=|\\|\\||:|=>|\\?=|,)[\\x60'\"\\s=]{0,5}([a-z0-9]{40})(?:[\\x60'\"\\s;]|\\\\[nr]|$)", "keywords": ["zendesk"]}]}
|
package/redact.mjs
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// Text leaving this machine for a Teams server is redacted first: a secret pasted into a card, a session or a mail is
|
|
2
|
+
// replaced by a marker naming what it was (`[redacted:<variable>]`, `[redacted:<rule>]`), never its value. Two sources:
|
|
3
|
+
// - the values this machine keeps in its custody files (KEY=value lines), which no detector knows; the files are named
|
|
4
|
+
// in `<supercode home>/custody.json` ({ "files": [...] }) and in SUPERCODE_CUSTODY_FILES (path-separated);
|
|
5
|
+
// - gitleaks' default rule set, vendored unchanged as redact/gitleaks-rules.json (its source and sha256 inside): each
|
|
6
|
+
// rule's keywords gate it, its secret group is what is replaced, and its entropy floor is kept.
|
|
7
|
+
// Local files are never touched; only what is sent is redacted.
|
|
8
|
+
import { readFileSync, existsSync, statSync } from 'node:fs';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { delimiter, join } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
|
|
13
|
+
const vendored = JSON.parse(readFileSync(new URL('./redact/gitleaks-rules.json', import.meta.url), 'utf8'));
|
|
14
|
+
|
|
15
|
+
// A pattern's leading runs that may be empty (`[\w.-]{0,50}?`, 101 of gitleaks' rules, two in a row on the
|
|
16
|
+
// keyword-context rules) only widen the match to the left; with them gone the same secret groups are found, the match
|
|
17
|
+
// starting later. Go's RE2 runs them in linear time, but JavaScript backtracks every split of them at every start: two
|
|
18
|
+
// of them on a 5 MB tool result held the connector's one thread for minutes, its link with it.
|
|
19
|
+
const LEADING_RUN = /^(?:\[(?:\\.|[^\]\\])*\]|\\[wWsSdD]|\.)(?:\{0,\d+\}|\*)\??/;
|
|
20
|
+
// Only into groups that capture nothing: a capturing group may be the secret, which must be replaced whole.
|
|
21
|
+
const OPENER = /^(?:\(\?i\)|\(\?i:|\(\?:)/;
|
|
22
|
+
function dropLeadingRuns(source) {
|
|
23
|
+
let src = source, head = 0;
|
|
24
|
+
for (;;) {
|
|
25
|
+
const opener = OPENER.exec(src.slice(head));
|
|
26
|
+
if (opener) { head += opener[0].length; continue; }
|
|
27
|
+
const run = LEADING_RUN.exec(src.slice(head));
|
|
28
|
+
if (!run) return src;
|
|
29
|
+
src = src.slice(0, head) + src.slice(head + run[0].length);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Go's `(?i)` in the middle of a pattern applies to the rest of its group; JavaScript writes that as `(?i:…)`.
|
|
34
|
+
function jsPattern(source) {
|
|
35
|
+
let flags = 'g', src = dropLeadingRuns(source);
|
|
36
|
+
if (src.startsWith('(?i)')) { src = src.slice(4); flags += 'i'; }
|
|
37
|
+
let out = '', depth = 0; const close = [];
|
|
38
|
+
for (let i = 0; i < src.length; i += 1) {
|
|
39
|
+
const ch = src[i];
|
|
40
|
+
if (ch === '\\') { out += ch + (src[i + 1] ?? ''); i += 1; continue; }
|
|
41
|
+
if (ch === '[') { const end = src.indexOf(']', i + 2); out += src.slice(i, end + 1); i = end; continue; }
|
|
42
|
+
if (src.startsWith('(?i)', i)) { out += '(?i:'; close.push(depth); i += 3; continue; }
|
|
43
|
+
if (ch === '(') depth += 1;
|
|
44
|
+
if (ch === ')') { while (close.length && close.at(-1) === depth) { out += ')'; close.pop(); } depth -= 1; }
|
|
45
|
+
out += ch;
|
|
46
|
+
}
|
|
47
|
+
while (close.length) { out += ')'; close.pop(); }
|
|
48
|
+
try { return new RegExp(out, flags); } catch { /* a Node without modifier groups: case-insensitive throughout */ }
|
|
49
|
+
try { return new RegExp(src.replaceAll('(?i)', ''), flags.includes('i') ? flags : `${flags}i`); } catch { return null; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const entropy = (text) => { const counts = {}; for (const ch of text) counts[ch] = (counts[ch] ?? 0) + 1; let h = 0; for (const n of Object.values(counts)) { const p = n / text.length; h -= p * Math.log2(p); } return h; };
|
|
53
|
+
// The longest text a pattern can match (Infinity when a run is unbounded): a rule whose match is bounded and must
|
|
54
|
+
// contain one of its keywords is run only on the text around each place a keyword occurs, not on the whole string.
|
|
55
|
+
function longestMatch(source) {
|
|
56
|
+
let i = 0;
|
|
57
|
+
const atom = () => {
|
|
58
|
+
const ch = source[i];
|
|
59
|
+
if (ch === '\\') {
|
|
60
|
+
const next = source[i + 1]; i += 2;
|
|
61
|
+
if (next === 'x') { i += 2; return 1; }
|
|
62
|
+
if (next === 'u') { if (source[i] === '{') i = source.indexOf('}', i) + 1; else i += 4; return 1; }
|
|
63
|
+
if (next === 'b' || next === 'B') return 0;
|
|
64
|
+
if (/[1-9]/.test(next)) return Infinity;
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
if (ch === '[') { i += 1; if (source[i] === '^') i += 1; if (source[i] === ']') i += 1; while (i < source.length && source[i] !== ']') i += source[i] === '\\' ? 2 : 1; i += 1; return 1; }
|
|
68
|
+
if (ch === '(') {
|
|
69
|
+
i += 1; let look = false;
|
|
70
|
+
if (source[i] === '?') {
|
|
71
|
+
if (source.startsWith('?=', i) || source.startsWith('?!', i)) { look = true; i += 2; }
|
|
72
|
+
else if (source.startsWith('?<=', i) || source.startsWith('?<!', i)) { look = true; i += 3; }
|
|
73
|
+
else if (source[i + 1] === '<') i = source.indexOf('>', i) + 1;
|
|
74
|
+
else { const colon = source.indexOf(':', i); i = colon + 1; }
|
|
75
|
+
}
|
|
76
|
+
const inner = alternatives(); i += 1; // ')'
|
|
77
|
+
return look ? 0 : inner;
|
|
78
|
+
}
|
|
79
|
+
if (ch === '^' || ch === '$') { i += 1; return 0; }
|
|
80
|
+
i += 1; return 1;
|
|
81
|
+
};
|
|
82
|
+
const sequence = () => {
|
|
83
|
+
let total = 0;
|
|
84
|
+
while (i < source.length && source[i] !== '|' && source[i] !== ')') {
|
|
85
|
+
let one = atom();
|
|
86
|
+
const q = source[i];
|
|
87
|
+
if (q === '*' || q === '+') { one = one ? Infinity : 0; i += 1; }
|
|
88
|
+
else if (q === '?') { i += 1; }
|
|
89
|
+
else if (q === '{' && /^\{\d+(,\d*)?\}/.test(source.slice(i))) {
|
|
90
|
+
const [, n, m] = /^\{(\d+)(?:,(\d*))?\}/.exec(source.slice(i));
|
|
91
|
+
const most = m === undefined ? Number(n) : m === '' ? Infinity : Number(m);
|
|
92
|
+
one = one === 0 ? 0 : one * most; i = source.indexOf('}', i) + 1;
|
|
93
|
+
}
|
|
94
|
+
if (source[i] === '?') i += 1; // lazy
|
|
95
|
+
total += one;
|
|
96
|
+
}
|
|
97
|
+
return total;
|
|
98
|
+
};
|
|
99
|
+
const alternatives = () => { let most = sequence(); while (source[i] === '|') { i += 1; most = Math.max(most, sequence()); } return most; };
|
|
100
|
+
try { const most = alternatives(); return i >= source.length ? most : Infinity; } catch { return Infinity; }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const RULES = vendored.rules.map((rule) => { const regex = jsPattern(rule.regex); return { id: rule.id, regex, longest: regex ? longestMatch(regex.source) : Infinity, group: rule.secretGroup ?? null, keywords: (rule.keywords ?? []).map((k) => k.toLowerCase()), entropy: rule.entropy ?? null }; }).filter((rule) => rule.regex);
|
|
104
|
+
export const RULESET = { source: vendored.source, sha256: vendored.sha256, rules: RULES.length };
|
|
105
|
+
|
|
106
|
+
// Where each of many literals occurs, in one pass over the text (gitleaks finds its keywords the same way, with one
|
|
107
|
+
// automaton): a lookahead finds every position some literal starts at, and only those are compared literal by literal.
|
|
108
|
+
// Hundreds of separate includes() read a 5 MB tool result hundreds of times.
|
|
109
|
+
const escapeLiteral = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
110
|
+
function literalScanner(literals) {
|
|
111
|
+
const unique = [...new Set(literals)].filter(Boolean);
|
|
112
|
+
if (!unique.length) return () => new Map();
|
|
113
|
+
const starts = new RegExp(`(?=(?:${[...unique].sort((a, b) => b.length - a.length).map(escapeLiteral).join('|')}))`, 'g');
|
|
114
|
+
return (text) => {
|
|
115
|
+
const hits = new Map();
|
|
116
|
+
for (const { index } of text.matchAll(starts)) for (const literal of unique) if (text.startsWith(literal, index)) { if (!hits.has(literal)) hits.set(literal, []); hits.get(literal).push(index); }
|
|
117
|
+
return hits;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const keywordHits = literalScanner(RULES.flatMap((rule) => rule.keywords));
|
|
121
|
+
const EVERY_RULE_KEYED = RULES.every((rule) => rule.keywords.length);
|
|
122
|
+
const anyKeyword = new RegExp([...new Set(RULES.flatMap((rule) => rule.keywords))].sort((a, b) => b.length - a.length).map(escapeLiteral).join('|') || '(?!)');
|
|
123
|
+
|
|
124
|
+
// The custody values are read again when custody.json or a file it names changes (looked at once every
|
|
125
|
+
// CUSTODY_LOOK_MS): a value added while a connector runs is redacted from then on, not from its next restart.
|
|
126
|
+
const CUSTODY_LOOK_MS = 30_000;
|
|
127
|
+
let custodyCache = null, custodyStamp = null, custodyLookedAt = 0;
|
|
128
|
+
const stampOf = (file) => { try { const s = statSync(file); return `${s.ino}:${s.size}:${s.mtimeMs}`; } catch { return '-'; } };
|
|
129
|
+
function custodyFiles(env) {
|
|
130
|
+
const home = env.SUPERCODE_HOME || join(env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'supercode');
|
|
131
|
+
let files = String(env.SUPERCODE_CUSTODY_FILES ?? '').split(delimiter).filter(Boolean);
|
|
132
|
+
try { files = files.concat(JSON.parse(readFileSync(join(home, 'custody.json'), 'utf8')).files ?? []); } catch { /* none named */ }
|
|
133
|
+
return { files, stamp: [join(home, 'custody.json'), ...files].map(stampOf).join('|') };
|
|
134
|
+
}
|
|
135
|
+
/** Each custody value (at least 8 characters) with the variable that holds it, longest first. */
|
|
136
|
+
export function custodyValues(env = process.env) {
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
if (custodyCache && now - custodyLookedAt < CUSTODY_LOOK_MS) return custodyCache;
|
|
139
|
+
custodyLookedAt = now;
|
|
140
|
+
const { files, stamp } = custodyFiles(env);
|
|
141
|
+
if (custodyCache && stamp === custodyStamp) return custodyCache;
|
|
142
|
+
custodyStamp = stamp;
|
|
143
|
+
const values = new Map();
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
let text = ''; try { if (existsSync(file)) text = readFileSync(file, 'utf8'); } catch { continue; }
|
|
146
|
+
for (const line of text.split('\n')) {
|
|
147
|
+
const m = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/.exec(line);
|
|
148
|
+
if (!m) continue;
|
|
149
|
+
const value = m[2].trim().replace(/^(['"])(.*)\1$/, '$2');
|
|
150
|
+
// A custody file also holds plain settings (a URL, a host, a number): only a secret is a value to redact, by its
|
|
151
|
+
// name (…KEY, …TOKEN, …SECRET, …PASSWORD, …COOKIE, …CREDENTIAL…) or by being long and random.
|
|
152
|
+
const named = /(?:KEY|TOKEN|SECRET|PASSW|COOKIE|CREDENTIAL|PRIVATE|AUTH|SESSION|DSN|WEBHOOK)/i.test(m[1]);
|
|
153
|
+
const plain = /^(?:https?:\/\/|\/|~|[0-9.:,_-]+$|true$|false$)/i.test(value);
|
|
154
|
+
if (value.length >= 8 && !plain && (named || (value.length >= 20 && entropy(value) >= 3.5)) && !values.has(value)) values.set(value, m[1]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
custodyCache = [...values].map(([value, name]) => ({ value, name })).sort((a, b) => b.value.length - a.value.length);
|
|
158
|
+
custodyBasis = createHash('sha256').update(JSON.stringify([RULESET.sha256, custodyCache.map(({ value }) => value)])).digest('hex');
|
|
159
|
+
return custodyCache;
|
|
160
|
+
}
|
|
161
|
+
let custodyBasis = null;
|
|
162
|
+
/** What a redaction was made under (the rule set and the custody values), as a digest: text found clean under one
|
|
163
|
+
* basis is clean under it again, and needs another look only when it changes. */
|
|
164
|
+
export function redactionBasis(env = process.env) { custodyValues(env); return custodyBasis; }
|
|
165
|
+
|
|
166
|
+
// Text this far either side of a keyword is searched by a bounded rule, beyond its longest match.
|
|
167
|
+
const WINDOW_MARGIN = 256;
|
|
168
|
+
/** The spans of a text within `reach` of an occurrence (`hits`) of one of `keywords`, merged. */
|
|
169
|
+
function keywordSpans(hits, length, keywords, reach) {
|
|
170
|
+
const spans = [];
|
|
171
|
+
for (const keyword of keywords) for (const at of hits.get(keyword) ?? []) spans.push([Math.max(0, at - reach), Math.min(length, at + keyword.length + reach)]);
|
|
172
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
173
|
+
const merged = [];
|
|
174
|
+
for (const span of spans) { const last = merged.at(-1); if (last && span[0] <= last[1]) last[1] = Math.max(last[1], span[1]); else merged.push(span); }
|
|
175
|
+
return merged;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** `text` with every secret replaced by its marker. */
|
|
179
|
+
export function redactText(text, env = process.env) {
|
|
180
|
+
if (typeof text !== 'string' || text.length < 8) return text;
|
|
181
|
+
let out = text;
|
|
182
|
+
// Custody values are random, so one alternation of them gives the regex engine nothing to skip on: each is looked
|
|
183
|
+
// for on its own, which reads a 5 MB text in a few dozen milliseconds.
|
|
184
|
+
for (const { value, name } of custodyValues(env)) if (out.includes(value)) out = out.split(value).join(`[redacted:${name}]`);
|
|
185
|
+
// Every rule has keywords, so a text holding none of them is done (the one test is cheap; finding where each keyword
|
|
186
|
+
// occurs is not, and most texts are short and hold none).
|
|
187
|
+
let lower = out.toLowerCase();
|
|
188
|
+
if (EVERY_RULE_KEYED && !anyKeyword.test(lower)) return out;
|
|
189
|
+
let hits = keywordHits(lower);
|
|
190
|
+
for (const rule of RULES) {
|
|
191
|
+
if (rule.keywords.length && !rule.keywords.some((k) => hits.has(k))) continue;
|
|
192
|
+
const replace = (text) => {
|
|
193
|
+
rule.regex.lastIndex = 0;
|
|
194
|
+
return text.replace(rule.regex, (...match) => {
|
|
195
|
+
// as gitleaks reads a rule: its secret group, else its first group, else the whole match
|
|
196
|
+
const whole = match[0], secret = rule.group ? match[rule.group] : typeof match[1] === 'string' ? match[1] : whole;
|
|
197
|
+
if (!secret || secret.startsWith('[redacted:')) return whole;
|
|
198
|
+
if (rule.entropy && entropy(secret) < rule.entropy) return whole;
|
|
199
|
+
return whole.replace(secret, `[redacted:${rule.id}]`);
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
// gitleaks runs a rule whose keyword occurs over the whole text; a bounded rule's match holds a keyword, so here
|
|
203
|
+
// it runs only around each one (lowercasing that changes a length, outside ASCII, falls back to the whole text).
|
|
204
|
+
const spans = rule.keywords.length && Number.isFinite(rule.longest) && lower.length === out.length ? keywordSpans(hits, out.length, rule.keywords, rule.longest + WINDOW_MARGIN) : null;
|
|
205
|
+
let next;
|
|
206
|
+
if (!spans) next = replace(out);
|
|
207
|
+
else { next = ''; let from = 0; for (const [start, end] of spans) { next += out.slice(from, start) + replace(out.slice(start, end)); from = end; } next += out.slice(from); }
|
|
208
|
+
if (next !== out) { out = next; lower = out.toLowerCase(); hits = keywordHits(lower); }
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** A JSON value with every string in it redacted (keys kept). */
|
|
214
|
+
export function redactValue(value, env = process.env) {
|
|
215
|
+
if (typeof value === 'string') return redactText(value, env);
|
|
216
|
+
if (Array.isArray(value)) return value.map((item) => redactValue(item, env));
|
|
217
|
+
if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, redactValue(item, env)]));
|
|
218
|
+
return value;
|
|
219
|
+
}
|
package/session-follow.mjs
CHANGED
|
@@ -163,6 +163,16 @@ const automationImage = seed => `https://brand.volter.ai/bot/svg?seed=${encodeUR
|
|
|
163
163
|
* may draw its own faces; `personImage`/`agentImage` choose the default pictures. One author's run within five minutes
|
|
164
164
|
* is `continued`, and each entry has a short local `timeLabel`.
|
|
165
165
|
*/
|
|
166
|
+
/**
|
|
167
|
+
* A Room member's words, without the envelope Teams types ahead of them into the session ('[Room "<name>" ("room_…");
|
|
168
|
+
* sender "<name>" ("prn_…")] ' then the words). Only that exact quote is taken off, and only when the sender it names is
|
|
169
|
+
* the principal recorded as typing the turn; any other text, a forged or quoted envelope included, is shown as typed.
|
|
170
|
+
*/
|
|
171
|
+
function roomWords(text, principal) {
|
|
172
|
+
const m = /^\s*\[Room "(?:[^"\\]|\\.)*" \("room_[A-Za-z0-9]+"\); sender "(?:[^"\\]|\\.)*" \("(prn_[A-Za-z0-9]+)"\)\]\s*/.exec(String(text ?? ''));
|
|
173
|
+
return m && principal && m[1] === principal ? String(text).slice(m[0].length) : text;
|
|
174
|
+
}
|
|
175
|
+
|
|
166
176
|
export function withPeople(transcript, messages, { agentName = null, sessionId = '', personImage: person = personImage, agentImage: agent = agentImage, locale = [] } = {}) {
|
|
167
177
|
const byTime = new Map();
|
|
168
178
|
for (const message of messages) {
|
|
@@ -204,7 +214,8 @@ export function withPeople(transcript, messages, { agentName = null, sessionId =
|
|
|
204
214
|
const seed = name ? (metadata.source_principal_id || name) : '';
|
|
205
215
|
const via = typeof metadata.source_via === 'string' ? metadata.source_via.trim() : '';
|
|
206
216
|
const source = ({ room: 'Room', app: 'App', terminal: 'Terminal', api: 'API' })[via] || via || 'Session';
|
|
207
|
-
|
|
217
|
+
const text = via === 'room' ? roomWords(entry.text, metadata.source_principal_id) : entry.text;
|
|
218
|
+
return { ...entry, text, actor: { ...(seed ? { id: seed } : {}), name: name ?? 'Anonymous', anonymous: !name, source, image: person(seed), kind: 'person' } };
|
|
208
219
|
});
|
|
209
220
|
let last = null;
|
|
210
221
|
return people.map((entry) => {
|
package/viewer/session-view.css
CHANGED
|
@@ -669,6 +669,8 @@
|
|
|
669
669
|
.scui-configuration-value-input { flex:1; min-width:calc(120px * var(--scui-scaling)); width:100%; }
|
|
670
670
|
.scui-configuration-button { padding:calc(5px * var(--scui-scaling)) calc(12px * var(--scui-scaling)); }
|
|
671
671
|
.scui-configuration-alert { color:var(--scui-danger,var(--scui-fg)); }
|
|
672
|
+
.scui-configuration-proposed { color:var(--scui-muted,var(--scui-fg)); }
|
|
673
|
+
.scui-configuration-proposed-link { color:var(--scui-accent,currentColor); }
|
|
672
674
|
.scui-configuration-details { margin-top:calc(20px * var(--scui-scaling)); color:var(--scui-muted); }
|
|
673
675
|
.scui-configuration-summary { cursor:pointer; }
|
|
674
676
|
.scui-configuration-input { box-sizing:border-box; min-width:0; max-width:100%; padding:calc(7px * var(--scui-scaling)) calc(9px * var(--scui-scaling)); }
|
|
@@ -704,6 +706,43 @@
|
|
|
704
706
|
.scui-settings-text { margin:0 }
|
|
705
707
|
.scui-settings-footer { display:flex; gap:calc(6px * var(--scui-scaling)); justify-content:flex-end; margin-top:auto; padding:calc(8px * var(--scui-scaling)); border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }
|
|
706
708
|
.scui-settings-action { padding:calc(6px * var(--scui-scaling)) calc(9px * var(--scui-scaling)); border-radius:var(--scui-button-radius, var(--scui-radius-md)) }
|
|
709
|
+
.scui-setup-jobs { display:grid; gap:6px; margin:0 0 8px; padding:0; list-style:none; }
|
|
710
|
+
.scui-timeline { display:grid; gap:8px; min-width:0; font-size:12.5px; }
|
|
711
|
+
.scui-timeline-header { display:flex; gap:4px; align-items:center; }
|
|
712
|
+
.scui-timeline-button { min-width:28px; padding:2px 8px; }
|
|
713
|
+
.scui-timeline-grid { position:relative; display:grid; gap:2px; min-width:0; }
|
|
714
|
+
.scui-timeline-axis { position:relative; height:18px; margin-left:var(--scui-timeline-label-width,180px); border-bottom:1px solid var(--scui-border); }
|
|
715
|
+
.scui-timeline-tick { position:absolute; transform:translateX(-50%); color:var(--scui-muted); font-size:11px; white-space:nowrap; }
|
|
716
|
+
.scui-timeline-lane { display:grid; grid-template-columns:var(--scui-timeline-label-width,180px) 1fr; align-items:center; min-height:24px; }
|
|
717
|
+
.scui-timeline-lane-label { overflow:hidden; padding-right:8px; color:var(--scui-fg); text-overflow:ellipsis; white-space:nowrap; }
|
|
718
|
+
.scui-timeline-track { position:relative; height:20px; border-radius:4px; background:var(--scui-bg-raised); }
|
|
719
|
+
.scui-timeline-block { position:absolute; top:2px; height:16px; min-width:2px; overflow:hidden; padding:0 4px; border:0; border-radius:3px; background:var(--scui-accent); color:var(--scui-bg); font-size:11px; text-align:left; white-space:nowrap; }
|
|
720
|
+
.scui-timeline-block[data-kind="forecast"] { border:1px dashed var(--scui-accent); background:transparent; color:var(--scui-fg); }
|
|
721
|
+
.scui-timeline-block[data-kind="due"] { width:3px !important; padding:0; background:var(--scui-danger); }
|
|
722
|
+
.scui-timeline-block[data-kind="failed"] { background:var(--scui-danger); }
|
|
723
|
+
.scui-timeline-block[data-kind="cancelled"] { background:var(--scui-muted); }
|
|
724
|
+
.scui-timeline-block[data-kind="waiting"] { background:var(--scui-warning); }
|
|
725
|
+
.scui-timeline-block[data-kind="idle"] { background:var(--scui-fill); color:var(--scui-fg); }
|
|
726
|
+
.scui-timeline-block-label { pointer-events:none; }
|
|
727
|
+
.scui-timeline-overlay { position:absolute; inset:18px 0 0 var(--scui-timeline-label-width,180px); width:calc(100% - var(--scui-timeline-label-width,180px)); height:calc(100% - 18px); overflow:visible; pointer-events:none; }
|
|
728
|
+
.scui-timeline-arrow { fill:none; stroke:var(--scui-muted); stroke-width:1.2; }
|
|
729
|
+
.scui-timeline-now { stroke:var(--scui-danger); stroke-width:1; }
|
|
730
|
+
.scui-timeline-series { display:grid; grid-template-columns:var(--scui-timeline-label-width,180px) 1fr; align-items:center; height:48px; }
|
|
731
|
+
.scui-timeline-series-chart { width:100%; height:44px; }
|
|
732
|
+
.scui-timeline-series-line { fill:none; stroke:var(--scui-accent); stroke-width:1.5; }
|
|
733
|
+
.scui-calendar { display:grid; gap:8px; font-size:12.5px; }
|
|
734
|
+
.scui-calendar-header { display:flex; gap:8px; align-items:center; }
|
|
735
|
+
.scui-calendar-title { min-width:140px; text-align:center; }
|
|
736
|
+
.scui-calendar-grid { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:2px; margin:0; padding:0; list-style:none; }
|
|
737
|
+
.scui-calendar-day { display:grid; align-content:start; gap:2px; min-height:84px; padding:4px; border-radius:4px; background:var(--scui-bg-raised); }
|
|
738
|
+
.scui-calendar-day[data-outside="true"] { opacity:.45; }
|
|
739
|
+
.scui-calendar-day[data-today="true"] { outline:1px solid var(--scui-accent); }
|
|
740
|
+
.scui-calendar-date { color:var(--scui-muted); font-size:11px; }
|
|
741
|
+
.scui-calendar-entry { overflow:hidden; padding:1px 4px; border:0; border-radius:3px; background:var(--scui-accent); color:var(--scui-bg); font-size:11px; text-align:left; text-overflow:ellipsis; white-space:nowrap; }
|
|
742
|
+
.scui-calendar-entry[data-kind="deadline"] { background:var(--scui-danger); }
|
|
743
|
+
.scui-calendar-entry[data-kind="window"] { background:var(--scui-warning); }
|
|
744
|
+
.scui-calendar-entry[data-kind="job"] { background:var(--scui-fill); color:var(--scui-fg); }
|
|
745
|
+
.scui-calendar-empty { margin:0; color:var(--scui-muted); }
|
|
707
746
|
}
|
|
708
747
|
@layer scui.components {
|
|
709
748
|
/* Team view: sessions, machines, pane access and a pane's terminal over a Teams server. */
|