@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 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 { resolve } from 'node:path';
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
- if (!machine.startsWith('mach_')) {
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.10",
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
+ }
@@ -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
- return { ...entry, actor: { ...(seed ? { id: seed } : {}), name: name ?? 'Anonymous', anonymous: !name, source, image: person(seed), kind: 'person' } };
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) => {
@@ -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. */