Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/db/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ import { DEFAULT_COMMAND_AUTHORIZATION_POLICY, normalizeCommandAuthorizationPoli
import { normalizeContributorBlacklist } from "../settings/contributor-blacklist";
import { normalizeAutonomyPolicy, normalizeAutoMaintainPolicy, DEFAULT_AUTO_MAINTAIN_POLICY } from "../settings/autonomy";
import { decryptSecret, encryptSecret, sha256Hex } from "../utils/crypto";
import { PUBLIC_LOCAL_PATH_SCRUB_PATTERN } from "../signals/redaction";
import { jsonString, nowIso, parseJson, repoParts } from "../utils/json";

const MAX_STORED_BODY_CHARS = 4000;
Expand Down Expand Up @@ -5210,7 +5211,6 @@ const PRODUCT_USAGE_SENSITIVE_KEY =
/authorization|cookie|token|secret|password|private[_-]?key|source|body|diff|patch|prompt|raw[_-]?trust|trust[_-]?score|wallet|hotkey|coldkey|seed|mnemonic|local[_-]?path|repo[_-]?root|cwd|scoreability|reviewability|farming/i;
const PRODUCT_USAGE_SENSITIVE_VALUE =
/\b(seed phrase|mnemonic|private key|raw trust|trust score|wallet|hotkey|coldkey|scoreability|reviewability|farming|reward estimate|payout)\b/i;
const PRODUCT_USAGE_LOCAL_PATH = /(?:\/Users|\/home|\/tmp)\/[^\s"',;)]*|[A-Za-z]:\\Users\\[^\s"',;)]*/g;
const PRODUCT_USAGE_TOKEN_VALUE = /\b(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g;
const PRODUCT_USAGE_BEARER_VALUE = /\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi;

Expand Down Expand Up @@ -5255,7 +5255,7 @@ function sanitizeProductUsageJson(value: unknown, depth: number, actorRedactor:

function sanitizeProductUsageString(value: string, maxLength: number): string {
const redacted = value
.replace(PRODUCT_USAGE_LOCAL_PATH, "<redacted-path>")
.replace(PUBLIC_LOCAL_PATH_SCRUB_PATTERN, "<redacted-path>")
.replace(PRODUCT_USAGE_TOKEN_VALUE, "<redacted-token>")
.replace(PRODUCT_USAGE_BEARER_VALUE, "Bearer <redacted-token>");
if (PRODUCT_USAGE_SENSITIVE_VALUE.test(redacted)) return "<redacted>";
Expand Down
6 changes: 4 additions & 2 deletions src/services/agent-action-explanation-card.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { AgentActionBlockerCategory, AgentActionExplanationCard, AgentActionRecord } from "../types";
import { PUBLIC_LOCAL_PATH_SCRUB_PATTERN } from "../signals/redaction";

type AgentActionExplanationInput = Pick<
AgentActionRecord,
Expand All @@ -9,7 +10,7 @@ const BLOCKER_CATEGORY_ORDER: AgentActionBlockerCategory[] = ["branch", "account
const PUBLIC_FORBIDDEN_PATTERN =
/\b(wallets?|hotkeys?|coldkeys?|seed phrases?|mnemonics?|raw[-_\s]?trust scores?|trust scores?|private reviewability|reviewability internals?|private scoreability|scoreability|projected scores?|score(?:d|s|ability)?|public score estimates?|estimated scores?|score estimates?|score previews?|reward estimates?|payouts?|farming|reward optimization|private rankings?)\b/gi;
const PUBLIC_SCORE_DELTA_PATTERN = /\b(?:projected\s+)?score\w*(?:\s+\w+){0,4}\s+[-+]?\d+(?:\.\d+)?\s*->\s*[-+]?\d+(?:\.\d+)?\b/gi;
const TOKEN_OR_PATH_PATTERN = /\bgithub_pat_[A-Za-z0-9_]+|\bgh[pousr]_[A-Za-z0-9_]+|\/Users\/\S+|\/home\/\S+|\/tmp\/\S+|[A-Z]:\\Users\\\S+/gi;
const TOKEN_PATTERN = /\bgithub_pat_[A-Za-z0-9_]+|\bgh[pousr]_[A-Za-z0-9_]+/gi;

export function withAgentActionExplanationCard(action: AgentActionRecord): AgentActionRecord {
return { ...action, explanationCard: buildAgentActionExplanationCard(action) };
Expand Down Expand Up @@ -118,7 +119,8 @@ function categorizeBlocker(blocker: string): AgentActionBlockerCategory {

function sanitizePublicCardText(value: string): string {
return compactText(value)
.replace(TOKEN_OR_PATH_PATTERN, "<redacted>")
.replace(TOKEN_PATTERN, "<redacted>")
.replace(PUBLIC_LOCAL_PATH_SCRUB_PATTERN, "<redacted>")
.replace(PUBLIC_SCORE_DELTA_PATTERN, "private context")
.replace(PUBLIC_FORBIDDEN_PATTERN, "private context")
.replace(/private context(?:[,\s]+private context)+/gi, "private context")
Expand Down
4 changes: 2 additions & 2 deletions src/services/control-panel-roles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isAuthorizedGitHubSessionLogin } from "../auth/security";
import { getFreshOfficialMinerDetection, getRepository, listAllPullRequests, listInstallations, listRepositories } from "../db/repositories";
import type { ControlPanelRoleCard, ControlPanelRoleName, ControlPanelRoleSummary, InstallationRecord, PullRequestRecord, RepositoryRecord } from "../types";
import { redactPublicLocalPaths } from "../signals/redaction";
import { nowIso } from "../utils/json";

export type RoleSummaryInputs = {
Expand Down Expand Up @@ -289,8 +290,7 @@ function isMaintainerAssociation(value: string | null | undefined): boolean {
}

export function sanitizeRoleText(value: string): string {
const redacted = value
.replace(/(?:\/Users|\/home|\/tmp)\/[^\s"',;)]*|[A-Za-z]:\\Users\\[^\s"',;)]*/g, "<redacted-path>")
const redacted = redactPublicLocalPaths(value, "<redacted-path>")
.replace(/\b(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g, "<redacted-token>")
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, "Bearer <redacted-token>");
if (/\b(seed phrase|mnemonic|private key|raw trust|trust score|wallet|hotkey|coldkey|payout|reward estimate|farming|private reviewability|public score estimate)\b/i.test(redacted)) return "<redacted>";
Expand Down
4 changes: 2 additions & 2 deletions src/services/miner-dashboard-recommendations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ContributorDecisionPack } from "./decision-pack";
import { PUBLIC_LOCAL_PATH_SCRUB_PATTERN } from "../signals/redaction";
import type { SignalSnapshotRecord } from "../types";

export type MinerDashboardSignalGroup = "repo_state" | "contributor_state" | "validation_state" | "policy_context";
Expand Down Expand Up @@ -42,7 +43,6 @@ const CHANGE_LABEL_LIMIT = 6;
const REASON_LIMIT = 3;
const FORBIDDEN_PUBLIC_TEXT =
/\b(wallets?|hotkeys?|coldkeys?|seed phrases?|mnemonics?|private keys?|raw[-_\s]?trust(?: scores?)?|trust[-_\s]?scores?|reward(?:[-_\s]?(?:estimate|prediction|claim|score))?s?|payouts?|farming(?:[-_\s]?language)?|private[-_\s]?reviewability|private[-_\s]?scoreability|scoreability|public[-_\s]?score[-_\s]?(?:estimate|prediction)|estimated[-_\s]?score|score[-_\s]?estimate)\b/gi;
const LOCAL_PATH = /(?:\/(?:Users|home|root|tmp|var)\/[^\s,;:)]+|[A-Za-z]:\\Users\\[^\s,;:)]+)/g;
const FORBIDDEN_TOKEN = /\b(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g;

export function previousDecisionPackFromSnapshots(currentPack: ContributorDecisionPack, snapshots: SignalSnapshotRecord[]): ContributorDecisionPack | undefined {
Expand Down Expand Up @@ -383,7 +383,7 @@ function numberValue(record: DashboardRecord | undefined, key: string): number |

function sanitizePublicText(value: string): string {
return value
.replace(LOCAL_PATH, "[local path]")
.replace(PUBLIC_LOCAL_PATH_SCRUB_PATTERN, "[local path]")
.replace(FORBIDDEN_TOKEN, "private context")
.replace(FORBIDDEN_PUBLIC_TEXT, "private context")
.replace(/\s+/g, " ")
Expand Down
4 changes: 2 additions & 2 deletions src/services/weekly-value-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "../db/repositories";
import { getLatestRegistrySnapshot } from "../registry/sync";
import { loadUpstreamStatus, type UpstreamStatus } from "../upstream/ruleset";
import { redactPublicLocalPaths } from "../signals/redaction";
import type {
InstallationHealthRecord,
InstallationRecord,
Expand Down Expand Up @@ -408,8 +409,7 @@ function normalizeReportDays(value: number | null | undefined): number {
}

function sanitizeReportText(value: string): string {
const redacted = value
.replace(/(?:\/Users|\/home|\/tmp)\/[^\s"',;)]*|[A-Za-z]:\\Users\\[^\s"',;)]*/g, "<redacted-path>")
const redacted = redactPublicLocalPaths(value, "<redacted-path>")
.replace(/\b(?:ghp_|github_pat_|gts_|glpat-|sk-)[A-Za-z0-9_=-]{8,}/g, "<redacted-token>")
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]{12,}/gi, "Bearer <redacted-token>");
if (
Expand Down
6 changes: 5 additions & 1 deletion src/signals/extension-contributor-context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ContributorOpportunity, PublicReadinessScore } from "./engine";
import { redactPublicLocalPaths } from "./redaction";

// ─── Contributor-context payloads for the browser extension (#556) ───────────────────────────────
// The contributor (miner) side of the extension overlay. Every payload here is PUBLIC-SAFE and self-
Expand All @@ -25,7 +26,10 @@ const FORBIDDEN_EXTENSION_TERMS =
/\b(?:rewards?|payouts?|farming|wallets?|hotkeys?|coldkeys?|seed[-\s]?phrases?|mnemonics?|private[-\s]?keys?|raw[-\s]?trust(?:[-\s]?scores?)?|trust[-\s]?scores?|score[-\s]?(?:estimate|preview|prediction)s?|estimated[-\s]?scores?|scoreability|private[-\s]?reviewability|reviewability[-\s]?internals?|private[-\s]?rankings?)\b/gi;

export function redactExtensionText(text: string): string {
return text.replace(FORBIDDEN_EXTENSION_TERMS, "[redacted]").replace(/\s+/g, " ").trim();
return redactPublicLocalPaths(text, "[redacted]")
.replace(FORBIDDEN_EXTENSION_TERMS, "[redacted]")
.replace(/\s+/g, " ")
.trim();
}

// ── issue-fit: "is this issue a good one for me to pick up?" ──────────────────────────────────────
Expand Down
8 changes: 7 additions & 1 deletion src/signals/focus-manifest.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PUBLIC_LOCAL_PATH_INLINE } from "./redaction";
import { parse as parseYaml } from "yaml";
import type { GatePolicyPack, GateRuleMode, JsonValue, RepositorySettings } from "../types";
import { normalizeAutonomyPolicy, normalizeAutoMaintainPolicy } from "../settings/autonomy";
Expand Down Expand Up @@ -240,8 +241,13 @@ const EMPTY_MANIFEST: FocusManifest = {
* Public-safe redaction guard shared with the local-branch packet renderer. Public manifest
* text must not leak reward, wallet/key, ranking, or local filesystem path material.
*/
const FOCUS_MANIFEST_PUBLIC_UNSAFE = new RegExp(
String.raw`\b(reward\w*|score\w*|wallets?|hotkeys?|coldkeys?|seed[-\s]?phrases?|mnemonics?|private[-\s]?keys?|farming|payouts?|rankings?|raw[-\s]?trust(?:[-\s]?scores?)?|trust[-\s]?scores?|private[-\s]?reviewability|reviewability(?:[-\s]?internals?)?|private[-\s]?scoreability|scoreability|public[-\s]?score[-\s]?(?:estimate|prediction|claim)s?|estimated[-\s]?scores?|score[-\s]?(?:estimate|prediction|preview)s?)\b|${PUBLIC_LOCAL_PATH_INLINE}`,
"i",
);

export function isFocusManifestPublicSafe(text: string): boolean {
return !/\b(reward\w*|score\w*|wallets?|hotkeys?|coldkeys?|seed[-\s]?phrases?|mnemonics?|private[-\s]?keys?|farming|payouts?|rankings?|raw[-\s]?trust(?:[-\s]?scores?)?|trust[-\s]?scores?|private[-\s]?reviewability|reviewability(?:[-\s]?internals?)?|private[-\s]?scoreability|scoreability|public[-\s]?score[-\s]?(?:estimate|prediction|claim)s?|estimated[-\s]?scores?|score[-\s]?(?:estimate|prediction|preview)s?)\b|\/Users\/|\/home\/|\/tmp\/|[A-Z]:\\Users\\/i.test(text);
return !FOCUS_MANIFEST_PUBLIC_UNSAFE.test(text);
}

function emptyManifest(source: FocusManifestSource, warnings: string[] = []): FocusManifest {
Expand Down
4 changes: 2 additions & 2 deletions src/signals/local-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { buildRepoRewardRisk, type RepoRewardRisk, type RewardRiskAction } from
import { buildLocalWorkspaceIntelligence, type LocalWorkspaceIntelligence } from "./local-workspace-intelligence";
import { buildFocusManifestGuidance, parseFocusManifest, type FocusManifestGuidance } from "./focus-manifest";
import { sanitizeLocalScorerWarnings } from "./local-scorer-diagnostics";
import { isPublicSafeText } from "./redaction";
import { isPublicSafeText, PUBLIC_LOCAL_PATH_PREFIX_PATTERN } from "./redaction";
import { deriveEligibilityPlan } from "../services/eligibility-plan";
import { scenarioInputFromLocalBranchMetadata } from "../scenarios/input-model";
import { renderPublicScenarioSummary, type PublicScenarioSummary, type ScenarioSummaryInput } from "../scenarios/scenario-summary";
Expand Down Expand Up @@ -1233,7 +1233,7 @@ function firstCommitTitle(messages: string[] | undefined): string | undefined {

function safeRepoPath(path: string): string {
/* v8 ignore next -- Empty path fallback protects malformed local-git adapters; path redaction is covered by local branch tests. */
return /^(\/Users\/|\/home\/|\/root\/|\/tmp\/|[A-Z]:\/Users\/)/i.test(String(path).replace(/\\/g, "/")) ? "[local path hidden]" : String(path || "(unknown path)").replace(/\\/g, "/");
return PUBLIC_LOCAL_PATH_PREFIX_PATTERN.test(String(path).replace(/\\/g, "/")) ? "[local path hidden]" : String(path || "(unknown path)").replace(/\\/g, "/");
}

export function isTestFile(file: string): boolean {
Expand Down
33 changes: 32 additions & 1 deletion src/signals/redaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,38 @@
// intentionally NOT collapsed onto `PUBLIC_UNSAFE_TERMS`.
export const PUBLIC_UNSAFE_TERMS = String.raw`(?:reward|score|wallet|hotkey|coldkey|mnemonic|payout|ranking)\w*|farming|raw[-_\s]?trust|trust[-_\s]?score|private[-_\s]?reviewability|reviewability`;

export const PUBLIC_UNSAFE_PATTERN = new RegExp(String.raw`\b(${PUBLIC_UNSAFE_TERMS})\b|/Users/|/home/|/root/|/tmp/|[A-Z]:[\\/]Users[\\/]`, "i");
/** Posix local path roots that must not appear on public surfaces. */
export const PUBLIC_LOCAL_PATH_ROOTS = String.raw`/Users/|/home/|/root/|/tmp/|/var/`;

/** Windows user-profile paths that must not appear on public surfaces. */
export const PUBLIC_LOCAL_PATH_WINDOWS = String.raw`[A-Z]:[\\/]Users[\\/]`;

/** Inline alternation for composing boundary patterns (non-global). */
export const PUBLIC_LOCAL_PATH_INLINE = `${PUBLIC_LOCAL_PATH_ROOTS}|${PUBLIC_LOCAL_PATH_WINDOWS}`;

/** Prefix test for absolute changed-file paths (anchored at start). */
export const PUBLIC_LOCAL_PATH_PREFIX_PATTERN = new RegExp(
String.raw`^(\/Users\/|\/home\/|\/root\/|\/tmp\/|\/var\/|[A-Z]:\/Users\/)`,
"i",
);

/** Global scrubber for known local path roots in free-form text. */
export const PUBLIC_LOCAL_PATH_SCRUB_PATTERN = new RegExp(
String.raw`(?:\/Users|\/home|\/root|\/tmp|\/var)\/[^\s"',;:)]*|[A-Za-z]:\\Users\\[^\s"',;)]*`,
"g",
);

export const PUBLIC_UNSAFE_PATTERN = new RegExp(String.raw`\b(${PUBLIC_UNSAFE_TERMS})\b|${PUBLIC_LOCAL_PATH_INLINE}`, "i");

/** True when `text` contains a known local filesystem path root. */
export function containsPublicLocalPath(text: string): boolean {
return new RegExp(PUBLIC_LOCAL_PATH_INLINE, "i").test(text);
}

/** Replace known local filesystem path roots with `replacement`. */
export function redactPublicLocalPaths(text: string, replacement = "<redacted-path>"): string {
return text.replace(PUBLIC_LOCAL_PATH_SCRUB_PATTERN, replacement);
}

/** True iff `text` contains nothing that must stay private — i.e. it is safe to surface on a public GitHub surface. */
export function isPublicSafeText(text: string): boolean {
Expand Down
5 changes: 5 additions & 0 deletions test/unit/extension-contributor-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ describe("redactExtensionText", () => {
it("leaves safe text untouched", () => {
expect(redactExtensionText("Maintainer-created issue, good fit.")).toBe("Maintainer-created issue, good fit.");
});

it("redacts local filesystem paths", () => {
expect(redactExtensionText("Evidence from /root/work/src/cache.ts")).toBe("Evidence from [redacted]");
expect(redactExtensionText("cache at /var/tmp/build")).toBe("cache at [redacted]");
});
});

describe("buildExtensionIssueFit", () => {
Expand Down
2 changes: 2 additions & 0 deletions test/unit/focus-manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,8 @@ describe("public-safe invariant", () => {
expect(isFocusManifestPublicSafe("Keep PRs focused")).toBe(true);
expect(isFocusManifestPublicSafe("estimate your reward")).toBe(false);
expect(isFocusManifestPublicSafe("paste your hotkey")).toBe(false);
expect(isFocusManifestPublicSafe("build from /root/work/repo")).toBe(false);
expect(isFocusManifestPublicSafe("cache under /var/tmp/build")).toBe(false);
});

it("never emits public next steps that contain forbidden language for generated manifests", () => {
Expand Down
22 changes: 22 additions & 0 deletions test/unit/local-branch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,28 @@ describe("local branch analysis", () => {
expect(analysis.prPacket.markdown).not.toContain("/root/work");
});

it("hides /var paths from public PR packet changed paths", () => {
const analysis = buildLocalBranchAnalysis({
input: {
login: "oktofeesh1",
repoFullName: repo.fullName,
body: "Fixes #7",
changedFiles: [{ path: "/var/tmp/build/src/cache.ts", additions: 12, deletions: 2, status: "modified" }],
validation: [{ command: "npm test -- cache", status: "passed" }],
},
repo,
issues: [{ repoFullName: repo.fullName, number: 7, title: "Cache refresh fails", state: "open", labels: ["bug"], linkedPrs: [] }],
pullRequests: [],
profile,
outcomeHistory,
scoringSnapshot,
scoringProfile,
});

expect(analysis.prPacket.markdown).toContain("[local path hidden]");
expect(analysis.prPacket.markdown).not.toContain("/var/tmp/build");
});

it("removes snake_case private signals from public PR packet markdown", () => {
const analysis = buildLocalBranchAnalysis({
input: {
Expand Down
6 changes: 6 additions & 0 deletions test/unit/policy-sanitizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ describe("sanitizeRoleText path redaction", () => {
expect(sanitizeRoleText("/tmp/deploy_key.pem")).toBe("<redacted-path>");
});

it("redacts root and var home paths entirely", () => {
expect(sanitizeRoleText("/root/work/repo")).toBe("<redacted-path>");
expect(sanitizeRoleText("/var/tmp/build/cache")).toBe("<redacted-path>");
expect(sanitizeRoleText("clone /root/work/repo here")).toBe("clone <redacted-path> here");
});

it("redacts Windows C:\\Users paths entirely", () => {
expect(sanitizeRoleText("C:\\Users\\bob\\AppData\\token.txt")).toBe("<redacted-path>");
});
Expand Down
25 changes: 24 additions & 1 deletion test/unit/redaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { describe, expect, it } from "vitest";
import { isPublicSafeText, PUBLIC_UNSAFE_PATTERN } from "../../src/signals/redaction";
import {
containsPublicLocalPath,
isPublicSafeText,
PUBLIC_LOCAL_PATH_PREFIX_PATTERN,
PUBLIC_UNSAFE_PATTERN,
redactPublicLocalPaths,
} from "../../src/signals/redaction";

describe("isPublicSafeText (#542 shared public/private boundary)", () => {
it("accepts text with no private signals", () => {
Expand Down Expand Up @@ -41,6 +47,7 @@ describe("isPublicSafeText (#542 shared public/private boundary)", () => {
expect(isPublicSafeText("/root/project/src")).toBe(false);
expect(isPublicSafeText("clone failed at /root/work/repo")).toBe(false);
expect(isPublicSafeText("/tmp/scratch")).toBe(false);
expect(isPublicSafeText("/var/log/app/build.log")).toBe(false);
expect(isPublicSafeText("C:\\Users\\carol\\repo")).toBe(false);
expect(isPublicSafeText("C:/Users/carol/repo")).toBe(false);
});
Expand All @@ -59,3 +66,19 @@ describe("isPublicSafeText (#542 shared public/private boundary)", () => {
expect(isPublicSafeText("clean line")).toBe(true);
});
});

describe("shared public local-path helpers", () => {
it("detects and redacts known local path roots", () => {
expect(containsPublicLocalPath("/root/work/repo")).toBe(true);
expect(containsPublicLocalPath("/var/folders/ci/cache")).toBe(true);
expect(containsPublicLocalPath("owner/repo")).toBe(false);
expect(redactPublicLocalPaths("clone /root/work/repo here")).toBe("clone <redacted-path> here");
expect(redactPublicLocalPaths("cache at /var/tmp/build")).toBe("cache at <redacted-path>");
});

it("matches absolute changed-file prefixes for safeRepoPath", () => {
expect(PUBLIC_LOCAL_PATH_PREFIX_PATTERN.test("/root/work/src/app.ts")).toBe(true);
expect(PUBLIC_LOCAL_PATH_PREFIX_PATTERN.test("/var/lib/cache.ts")).toBe(true);
expect(PUBLIC_LOCAL_PATH_PREFIX_PATTERN.test("src/app.ts")).toBe(false);
});
});
Loading