@ttt-productions/ttt-core 0.79.21 → 0.79.23

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.
@@ -19,6 +19,19 @@ export declare const FORMER_MEMBER_DISPLAY_NAME = "Former member";
19
19
  * both derive from it.
20
20
  */
21
21
  export declare const ACCOUNT_DELETION_GRACE_DAYS = 30;
22
+ /**
23
+ * Minimum length (inclusive) for a TTT account password. Counted in JavaScript
24
+ * string length units (UTF-16 code units), consistent with HTML input length
25
+ * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within
26
+ * the supported hosted password-policy range, which is set from this declaration.
27
+ */
28
+ export declare const PASSWORD_MIN_LENGTH = 7;
29
+ /**
30
+ * Maximum length (inclusive) for a TTT account password, in the same UTF-16
31
+ * code-unit units as the minimum. Long enough for passphrases and
32
+ * password-manager output while keeping the product input finite.
33
+ */
34
+ export declare const PASSWORD_MAX_LENGTH = 64;
22
35
  /** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */
23
36
  export declare const CRAFT_SKILL_LIMIT: number;
24
37
  /** Maximum number of work projects an artisan can own (steward) at once. */
@@ -1 +1 @@
1
- {"version":3,"file":"business-user.d.ts","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAM9C,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAInE,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAA0C,CAAC;AAE/E,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAA+C,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAIpC,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC"}
1
+ {"version":3,"file":"business-user.d.ts","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAKA,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,kBAAkB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAI9C;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAMtC,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAInE,4EAA4E;AAC5E,eAAO,MAAM,uBAAuB,QAA0C,CAAC;AAE/E,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,QAA+C,CAAC;AAEzF,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,6CAA6C;AAC7C,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAE/C,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAI/C,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAIpC,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAIrC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC"}
@@ -22,6 +22,20 @@ export const FORMER_MEMBER_DISPLAY_NAME = 'Former member';
22
22
  * both derive from it.
23
23
  */
24
24
  export const ACCOUNT_DELETION_GRACE_DAYS = 30;
25
+ // --- Account Passwords ---
26
+ /**
27
+ * Minimum length (inclusive) for a TTT account password. Counted in JavaScript
28
+ * string length units (UTF-16 code units), consistent with HTML input length
29
+ * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within
30
+ * the supported hosted password-policy range, which is set from this declaration.
31
+ */
32
+ export const PASSWORD_MIN_LENGTH = 7;
33
+ /**
34
+ * Maximum length (inclusive) for a TTT account password, in the same UTF-16
35
+ * code-unit units as the minimum. Long enough for passphrases and
36
+ * password-manager output while keeping the product input finite.
37
+ */
38
+ export const PASSWORD_MAX_LENGTH = 64;
25
39
  import { ACTIVE_LIMITS } from './app-mode.js';
26
40
  // --- User Profile Craft Skills ---
27
41
  /** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */
@@ -1 +1 @@
1
- {"version":3,"file":"business-user.js","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,+BAA+B;AAE/B,6BAA6B;AAE7B,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,oCAAoC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAEnE,yEAAyE;AAEzE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAI,CAAC,yBAAyB,CAAC;AAEzF,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,0BAA0B;AAE1B,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,iBAAiB;AAEjB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,gCAAgC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC","sourcesContent":["// User identity + profile business-rule constants — display names, craft skills,\n// mention history, and search.\n\n// --- User Display Names ---\n\n/** Minimum length for a user display name (inclusive). */\nexport const USERNAME_MIN_LENGTH = 3;\n\n/** Maximum length for a user display name (inclusive). */\nexport const USERNAME_MAX_LENGTH = 20;\n\n/** Allowed characters in a user display name: letters and numbers only. */\nexport const USERNAME_REGEX = /^[a-zA-Z0-9]+$/;\n\n/**\n * Display-name sentinel for an erased account (N3 data-deletion / GDPR erasure).\n * The `publicUsers/{uid}` doc is kept (so uid→name resolvers keep working) with\n * `displayName` set to this and `anonymizedAt` stamped; uid-keyed authorship /\n * share-tombstone records render with this label.\n */\nexport const FORMER_MEMBER_DISPLAY_NAME = 'Former member';\n\n/**\n * N3 account deletion / GDPR erasure — the grace window (days) between the deletion\n * request and the destructive scrub. Logging back in during the window is the one cancel.\n * Persisted on each request as `graceDays` (compliance trail) and stated as product policy\n * in the user-facing data-deletion documents — ONE declaration; the callable and the copy\n * both derive from it.\n */\nexport const ACCOUNT_DELETION_GRACE_DAYS = 30;\n\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- User Profile Craft Skills ---\n\n/** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */\nexport const CRAFT_SKILL_LIMIT = ACTIVE_LIMITS.user.maxCraftSkills;\n\n// --- Work-project participation caps (mode-varied, server-enforced) ---\n\n/** Maximum number of work projects an artisan can own (steward) at once. */\nexport const MAX_OWNED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxOwnedWorkProjects;\n\n/** Maximum number of active guild memberships a user can hold at once. */\nexport const MAX_ASSOCIATED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxAssociatedWorkProjects;\n\n/** Maximum number of tags allowed per craftSkill. */\nexport const MAX_CRAFT_SKILL_TAGS = 5;\n\n/** Maximum length for a craft-skill name. */\nexport const MAX_CRAFT_SKILL_NAME_LENGTH = 200;\n\n/** Non-US artisan-interest country/region free-text (input schema + privateData doc). */\nexport const MAX_ARTISAN_LOCATION_LENGTH = 100;\n\n// --- Mention History ---\n\n/** Maximum number of recent mentions kept in the user's mention-history hook. */\nexport const MAX_HISTORY_ITEMS = 10;\n\n// --- Search ---\n\n/** Maximum number of results returned by the search hook. */\nexport const SEARCH_RESULT_LIMIT = 6;\n\n// --- First-visit site tour ---\n\n/**\n * Current first-visit site-tour version. The `updateSiteTourPreference` callable stamps\n * this onto `privateData.siteTour.completedVersion` at completion, and the landing\n * eligibility check suppresses the automatic invitation while a member's\n * `completedVersion` equals it. Bump when the permanent-shell-controls tour changes\n * enough to re-invite every member. See\n * ttt-prod docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const SITE_TOUR_CURRENT_VERSION = 1;\n"]}
1
+ {"version":3,"file":"business-user.js","sourceRoot":"","sources":["../../src/constants/business-user.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,+BAA+B;AAE/B,6BAA6B;AAE7B,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,eAAe,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,EAAE,CAAC;AAE9C,4BAA4B;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,oCAAoC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC;AAEnE,yEAAyE;AAEzE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,uBAAuB,GAAG,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC;AAE/E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,4BAA4B,GAAG,aAAa,CAAC,IAAI,CAAC,yBAAyB,CAAC;AAEzF,qDAAqD;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAC;AAE/C,0BAA0B;AAE1B,iFAAiF;AACjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,iBAAiB;AAEjB,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,gCAAgC;AAEhC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC","sourcesContent":["// User identity + profile business-rule constants — display names, craft skills,\n// mention history, and search.\n\n// --- User Display Names ---\n\n/** Minimum length for a user display name (inclusive). */\nexport const USERNAME_MIN_LENGTH = 3;\n\n/** Maximum length for a user display name (inclusive). */\nexport const USERNAME_MAX_LENGTH = 20;\n\n/** Allowed characters in a user display name: letters and numbers only. */\nexport const USERNAME_REGEX = /^[a-zA-Z0-9]+$/;\n\n/**\n * Display-name sentinel for an erased account (N3 data-deletion / GDPR erasure).\n * The `publicUsers/{uid}` doc is kept (so uid→name resolvers keep working) with\n * `displayName` set to this and `anonymizedAt` stamped; uid-keyed authorship /\n * share-tombstone records render with this label.\n */\nexport const FORMER_MEMBER_DISPLAY_NAME = 'Former member';\n\n/**\n * N3 account deletion / GDPR erasure — the grace window (days) between the deletion\n * request and the destructive scrub. Logging back in during the window is the one cancel.\n * Persisted on each request as `graceDays` (compliance trail) and stated as product policy\n * in the user-facing data-deletion documents — ONE declaration; the callable and the copy\n * both derive from it.\n */\nexport const ACCOUNT_DELETION_GRACE_DAYS = 30;\n\n// --- Account Passwords ---\n\n/**\n * Minimum length (inclusive) for a TTT account password. Counted in JavaScript\n * string length units (UTF-16 code units), consistent with HTML input length\n * behavior and the Firebase Auth SDK. Above Firebase's hard floor of 6 and within\n * the supported hosted password-policy range, which is set from this declaration.\n */\nexport const PASSWORD_MIN_LENGTH = 7;\n\n/**\n * Maximum length (inclusive) for a TTT account password, in the same UTF-16\n * code-unit units as the minimum. Long enough for passphrases and\n * password-manager output while keeping the product input finite.\n */\nexport const PASSWORD_MAX_LENGTH = 64;\n\nimport { ACTIVE_LIMITS } from './app-mode.js';\n\n// --- User Profile Craft Skills ---\n\n/** Maximum number of craft-skills a user can upload to their profile. Mode-varied. */\nexport const CRAFT_SKILL_LIMIT = ACTIVE_LIMITS.user.maxCraftSkills;\n\n// --- Work-project participation caps (mode-varied, server-enforced) ---\n\n/** Maximum number of work projects an artisan can own (steward) at once. */\nexport const MAX_OWNED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxOwnedWorkProjects;\n\n/** Maximum number of active guild memberships a user can hold at once. */\nexport const MAX_ASSOCIATED_WORK_PROJECTS = ACTIVE_LIMITS.user.maxAssociatedWorkProjects;\n\n/** Maximum number of tags allowed per craftSkill. */\nexport const MAX_CRAFT_SKILL_TAGS = 5;\n\n/** Maximum length for a craft-skill name. */\nexport const MAX_CRAFT_SKILL_NAME_LENGTH = 200;\n\n/** Non-US artisan-interest country/region free-text (input schema + privateData doc). */\nexport const MAX_ARTISAN_LOCATION_LENGTH = 100;\n\n// --- Mention History ---\n\n/** Maximum number of recent mentions kept in the user's mention-history hook. */\nexport const MAX_HISTORY_ITEMS = 10;\n\n// --- Search ---\n\n/** Maximum number of results returned by the search hook. */\nexport const SEARCH_RESULT_LIMIT = 6;\n\n// --- First-visit site tour ---\n\n/**\n * Current first-visit site-tour version. The `updateSiteTourPreference` callable stamps\n * this onto `privateData.siteTour.completedVersion` at completion, and the landing\n * eligibility check suppresses the automatic invitation while a member's\n * `completedVersion` equals it. Bump when the permanent-shell-controls tour changes\n * enough to re-invite every member. See\n * ttt-prod docs/design/landing-backstage-guide-and-first-visit-plan.md §10.\n */\nexport const SITE_TOUR_CURRENT_VERSION = 1;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"safety-telemetry-patterns.d.ts","sourceRoot":"","sources":["../../src/constants/safety-telemetry-patterns.ts"],"names":[],"mappings":"AAoBA,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,iJAKxB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EA2B/D,CAAC"}
1
+ {"version":3,"file":"safety-telemetry-patterns.d.ts","sourceRoot":"","sources":["../../src/constants/safety-telemetry-patterns.ts"],"names":[],"mappings":"AAoBA,mFAAmF;AACnF,eAAO,MAAM,qBAAqB,iJAKxB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAqC/D,CAAC"}
@@ -41,6 +41,15 @@ export const TTT_FORBIDDEN_TELEMETRY_PATTERNS = [
41
41
  /\bnciiEvidence\/[^\s"']+/gi,
42
42
  /\bnciiAuthorityEvidence\/[^\s"']+/gi,
43
43
  /\bevidence\/[a-zA-Z0-9._-]+\/[a-f0-9]{16,}[^\s"']*/gi,
44
+ // --- Restricted SAFETY DOCUMENT paths carrying a statutory request id. ---
45
+ // A take-it-down request id and its request-scoped removal-deadline monitor id are
46
+ // restricted identifiers: Cloud Logging keeps them (the operator-only triage surface),
47
+ // Sentry never does. The app's own emit points are minimized by construction, but a
48
+ // THIRD-PARTY error message routinely embeds the document path it failed on — a Firestore
49
+ // NOT_FOUND names `safetySlaMonitors/{requestId}__nciiRemovalDeadline` verbatim — and that
50
+ // is precisely what this defense-in-depth layer exists to catch.
51
+ /\bsafetySlaMonitors\/[^\s"']+/gi,
52
+ /\btakeItDownRequests\/[^\s"']+/gi,
44
53
  // --- PhotoDNA detector-hash shape: a 64-hex string (sha256 / detector hash). ---
45
54
  /\b[a-f0-9]{64}\b/gi,
46
55
  // --- NCMEC credential markers. ---
@@ -1 +1 @@
1
- {"version":3,"file":"safety-telemetry-patterns.js","sourceRoot":"","sources":["../../src/constants/safety-telemetry-patterns.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,yEAAyE;AACzE,8EAA8E;AAC9E,4EAA4E;AAC5E,oEAAoE;AACpE,+EAA+E;AAC/E,qEAAqE;AACrE,uEAAuE;AACvE,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,iEAAiE;AACjE,sEAAsE;AACtE,4EAA4E;AAC5E,0EAA0E;AAC1E,gFAAgF;AAChF,+EAA+E;AAE/E,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,mCAAmC;IACnC,oCAAoC;IACpC,2BAA2B;IAC3B,4BAA4B;CACpB,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAwB;IACnE,uFAAuF;IACvF,4EAA4E;IAC5E,GAAG,qBAAqB,CAAC,GAAG,CAC1B,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,MAAM,CACR,qBAAqB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAC1D,IAAI,CACL,CACJ;IACD,uFAAuF;IACvF,sFAAsF;IAEtF,sEAAsE;IACtE,6FAA6F;IAC7F,gGAAgG;IAChG,4BAA4B;IAC5B,qCAAqC;IACrC,sDAAsD;IAEtD,kFAAkF;IAClF,oBAAoB;IAEpB,oCAAoC;IACpC,sFAAsF;IACtF,+CAA+C;IAC/C,qFAAqF;CACtF,CAAC","sourcesContent":["// ============================================================================\n// SAFETY TELEMETRY FORBIDDEN PATTERNS — the ONE declaration of the TTT-specific\n// forbidden-pattern set for the CSAM/NCII subsystem, injected into every\n// `createTelemetryScrubber({ patterns })` call (frontend client + nodejs/edge\n// inits AND the backend Sentry init). Consolidated here 2026-07-13 from the\n// former duplicate pair (functions/src/shared/safety-telemetry.ts ↔\n// src/lib/safety-telemetry-patterns.ts). Deliberately dependency-free: entries\n// are `RegExp | string`, structurally identical to monitoring-core's\n// `ForbiddenPattern`, so ttt-core needs no monitoring-core dependency.\n//\n// The patterns are SPECIFIC (→ low collateral on ordinary errors) and reference\n// the real storage prefixes + default project bucket hostnames used by the app:\n// - `nciiEvidence/`, `nciiAuthorityEvidence/` (public evidence key prefixes),\n// - `evidence/` (the SAFETY_EVIDENCE_BUCKET vault key prefix),\n// - the dev + prod default Firebase Storage buckets plus the legacy\n// `.appspot.com` form and the raw `storage.googleapis.com` object host.\n// Merged AFTER monitoring-core's generic DEFAULT_FORBIDDEN_PATTERNS (IPs,\n// bearer/authorization tokens, credential assignments) at every injection site.\n// ============================================================================\n\n/** The dev + prod default Firebase Storage bucket hostnames (apphosting*.yaml). */\nexport const EVIDENCE_BUCKET_HOSTS = [\n 'ttt-dev-cfb70.firebasestorage.app',\n 'ttt-prod-cfaf3.firebasestorage.app',\n 'ttt-dev-cfb70.appspot.com',\n 'ttt-prod-cfaf3.appspot.com',\n] as const;\n\n/**\n * TTT-specific forbidden patterns for the telemetry scrubber. `string` entries are\n * treated as literals by monitoring-core (no escaping needed); `RegExp` entries are\n * matched globally against every string in the event.\n */\nexport const TTT_FORBIDDEN_TELEMETRY_PATTERNS: (RegExp | string)[] = [\n // --- Evidence-bucket URL prefixes (dev + prod), any evidence/authority-proof key. ---\n // A fully-qualified evidence object URL: <host>/<...>/nciiEvidence/... etc.\n ...EVIDENCE_BUCKET_HOSTS.map(\n (host) =>\n new RegExp(\n `https?://[^\\\\s\"']*${host.replace(/\\./g, '\\\\.')}[^\\\\s\"']*`,\n 'gi',\n ),\n ),\n // The raw GCS object host, regardless of bucket (catches storage.googleapis.com URLs).\n /https?:\\/\\/(?:storage\\.googleapis\\.com|firebasestorage\\.googleapis\\.com)\\/[^\\s\"']*/gi,\n\n // --- Evidence storage KEY prefixes (bare object paths, no host). ---\n // nciiEvidence/{requestReference}/{fileId}, nciiAuthorityEvidence/{reservationKey}/{fileId},\n // and the vault key prefix evidence/{caseId}/{digest}. Match the prefix + the rest of the path.\n /\\bnciiEvidence\\/[^\\s\"']+/gi,\n /\\bnciiAuthorityEvidence\\/[^\\s\"']+/gi,\n /\\bevidence\\/[a-zA-Z0-9._-]+\\/[a-f0-9]{16,}[^\\s\"']*/gi,\n\n // --- PhotoDNA detector-hash shape: a 64-hex string (sha256 / detector hash). ---\n /\\b[a-f0-9]{64}\\b/gi,\n\n // --- NCMEC credential markers. ---\n // The PhotoDNA subscription-key header value + any NCMEC-credential-style assignment.\n /Ocp-Apim-Subscription-Key\\s*[:=]\\s*[^\\s\"']+/gi,\n /\\bncmec[_-]?(?:api[_-]?key|secret|password|credential|token)\\b\\s*[:=]?\\s*[^\\s\"']*/gi,\n];\n"]}
1
+ {"version":3,"file":"safety-telemetry-patterns.js","sourceRoot":"","sources":["../../src/constants/safety-telemetry-patterns.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,yEAAyE;AACzE,8EAA8E;AAC9E,4EAA4E;AAC5E,oEAAoE;AACpE,+EAA+E;AAC/E,qEAAqE;AACrE,uEAAuE;AACvE,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,iEAAiE;AACjE,sEAAsE;AACtE,4EAA4E;AAC5E,0EAA0E;AAC1E,gFAAgF;AAChF,+EAA+E;AAE/E,mFAAmF;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,mCAAmC;IACnC,oCAAoC;IACpC,2BAA2B;IAC3B,4BAA4B;CACpB,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAwB;IACnE,uFAAuF;IACvF,4EAA4E;IAC5E,GAAG,qBAAqB,CAAC,GAAG,CAC1B,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,MAAM,CACR,qBAAqB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAC1D,IAAI,CACL,CACJ;IACD,uFAAuF;IACvF,sFAAsF;IAEtF,sEAAsE;IACtE,6FAA6F;IAC7F,gGAAgG;IAChG,4BAA4B;IAC5B,qCAAqC;IACrC,sDAAsD;IAEtD,4EAA4E;IAC5E,mFAAmF;IACnF,uFAAuF;IACvF,oFAAoF;IACpF,0FAA0F;IAC1F,2FAA2F;IAC3F,iEAAiE;IACjE,iCAAiC;IACjC,kCAAkC;IAElC,kFAAkF;IAClF,oBAAoB;IAEpB,oCAAoC;IACpC,sFAAsF;IACtF,+CAA+C;IAC/C,qFAAqF;CACtF,CAAC","sourcesContent":["// ============================================================================\n// SAFETY TELEMETRY FORBIDDEN PATTERNS — the ONE declaration of the TTT-specific\n// forbidden-pattern set for the CSAM/NCII subsystem, injected into every\n// `createTelemetryScrubber({ patterns })` call (frontend client + nodejs/edge\n// inits AND the backend Sentry init). Consolidated here 2026-07-13 from the\n// former duplicate pair (functions/src/shared/safety-telemetry.ts ↔\n// src/lib/safety-telemetry-patterns.ts). Deliberately dependency-free: entries\n// are `RegExp | string`, structurally identical to monitoring-core's\n// `ForbiddenPattern`, so ttt-core needs no monitoring-core dependency.\n//\n// The patterns are SPECIFIC (→ low collateral on ordinary errors) and reference\n// the real storage prefixes + default project bucket hostnames used by the app:\n// - `nciiEvidence/`, `nciiAuthorityEvidence/` (public evidence key prefixes),\n// - `evidence/` (the SAFETY_EVIDENCE_BUCKET vault key prefix),\n// - the dev + prod default Firebase Storage buckets plus the legacy\n// `.appspot.com` form and the raw `storage.googleapis.com` object host.\n// Merged AFTER monitoring-core's generic DEFAULT_FORBIDDEN_PATTERNS (IPs,\n// bearer/authorization tokens, credential assignments) at every injection site.\n// ============================================================================\n\n/** The dev + prod default Firebase Storage bucket hostnames (apphosting*.yaml). */\nexport const EVIDENCE_BUCKET_HOSTS = [\n 'ttt-dev-cfb70.firebasestorage.app',\n 'ttt-prod-cfaf3.firebasestorage.app',\n 'ttt-dev-cfb70.appspot.com',\n 'ttt-prod-cfaf3.appspot.com',\n] as const;\n\n/**\n * TTT-specific forbidden patterns for the telemetry scrubber. `string` entries are\n * treated as literals by monitoring-core (no escaping needed); `RegExp` entries are\n * matched globally against every string in the event.\n */\nexport const TTT_FORBIDDEN_TELEMETRY_PATTERNS: (RegExp | string)[] = [\n // --- Evidence-bucket URL prefixes (dev + prod), any evidence/authority-proof key. ---\n // A fully-qualified evidence object URL: <host>/<...>/nciiEvidence/... etc.\n ...EVIDENCE_BUCKET_HOSTS.map(\n (host) =>\n new RegExp(\n `https?://[^\\\\s\"']*${host.replace(/\\./g, '\\\\.')}[^\\\\s\"']*`,\n 'gi',\n ),\n ),\n // The raw GCS object host, regardless of bucket (catches storage.googleapis.com URLs).\n /https?:\\/\\/(?:storage\\.googleapis\\.com|firebasestorage\\.googleapis\\.com)\\/[^\\s\"']*/gi,\n\n // --- Evidence storage KEY prefixes (bare object paths, no host). ---\n // nciiEvidence/{requestReference}/{fileId}, nciiAuthorityEvidence/{reservationKey}/{fileId},\n // and the vault key prefix evidence/{caseId}/{digest}. Match the prefix + the rest of the path.\n /\\bnciiEvidence\\/[^\\s\"']+/gi,\n /\\bnciiAuthorityEvidence\\/[^\\s\"']+/gi,\n /\\bevidence\\/[a-zA-Z0-9._-]+\\/[a-f0-9]{16,}[^\\s\"']*/gi,\n\n // --- Restricted SAFETY DOCUMENT paths carrying a statutory request id. ---\n // A take-it-down request id and its request-scoped removal-deadline monitor id are\n // restricted identifiers: Cloud Logging keeps them (the operator-only triage surface),\n // Sentry never does. The app's own emit points are minimized by construction, but a\n // THIRD-PARTY error message routinely embeds the document path it failed on — a Firestore\n // NOT_FOUND names `safetySlaMonitors/{requestId}__nciiRemovalDeadline` verbatim — and that\n // is precisely what this defense-in-depth layer exists to catch.\n /\\bsafetySlaMonitors\\/[^\\s\"']+/gi,\n /\\btakeItDownRequests\\/[^\\s\"']+/gi,\n\n // --- PhotoDNA detector-hash shape: a 64-hex string (sha256 / detector hash). ---\n /\\b[a-f0-9]{64}\\b/gi,\n\n // --- NCMEC credential markers. ---\n // The PhotoDNA subscription-key header value + any NCMEC-credential-style assignment.\n /Ocp-Apim-Subscription-Key\\s*[:=]\\s*[^\\s\"']+/gi,\n /\\bncmec[_-]?(?:api[_-]?key|secret|password|credential|token)\\b\\s*[:=]?\\s*[^\\s\"']*/gi,\n];\n"]}
@@ -1,2 +1,3 @@
1
1
  export * from './format.js';
2
+ export * from './password.js';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from './format.js';
2
+ export * from './password.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC","sourcesContent":["export * from './format.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC","sourcesContent":["export * from './format.js';\nexport * from './password.js';\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The one TTT account-password contract, shared by registration, password reset,
3
+ * and any future password surface. Length only — TTT enforces no composition
4
+ * requirements, so spaces, printable Unicode, and emoji are all allowed.
5
+ *
6
+ * Deliberately NOT named `validatePassword`: Firebase's own SDK exports a
7
+ * `validatePassword(auth, password)` that reads mutable hosted project policy.
8
+ * This helper is the stable product contract; the two must never be confused.
9
+ *
10
+ * The value is never trimmed, normalized, or otherwise mutated, and confirmation
11
+ * matching is the caller's job. Length is JavaScript string length (UTF-16 code
12
+ * units), matching HTML input length behavior and the Firebase SDK — a
13
+ * surrogate-pair emoji therefore counts as 2.
14
+ *
15
+ * Client validation is UX; the Firebase-enforced password policy remains the
16
+ * security boundary (ENG-006).
17
+ *
18
+ * @returns `null` when the password is acceptable (both boundaries inclusive),
19
+ * otherwise the user-facing reason it was rejected.
20
+ */
21
+ export declare function validateTttPassword(password: string): string | null;
22
+ //# sourceMappingURL=password.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnE"}
@@ -0,0 +1,31 @@
1
+ import { PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH } from '../constants/business-user.js';
2
+ /**
3
+ * The one TTT account-password contract, shared by registration, password reset,
4
+ * and any future password surface. Length only — TTT enforces no composition
5
+ * requirements, so spaces, printable Unicode, and emoji are all allowed.
6
+ *
7
+ * Deliberately NOT named `validatePassword`: Firebase's own SDK exports a
8
+ * `validatePassword(auth, password)` that reads mutable hosted project policy.
9
+ * This helper is the stable product contract; the two must never be confused.
10
+ *
11
+ * The value is never trimmed, normalized, or otherwise mutated, and confirmation
12
+ * matching is the caller's job. Length is JavaScript string length (UTF-16 code
13
+ * units), matching HTML input length behavior and the Firebase SDK — a
14
+ * surrogate-pair emoji therefore counts as 2.
15
+ *
16
+ * Client validation is UX; the Firebase-enforced password policy remains the
17
+ * security boundary (ENG-006).
18
+ *
19
+ * @returns `null` when the password is acceptable (both boundaries inclusive),
20
+ * otherwise the user-facing reason it was rejected.
21
+ */
22
+ export function validateTttPassword(password) {
23
+ if (password.length < PASSWORD_MIN_LENGTH) {
24
+ return `Password must be at least ${PASSWORD_MIN_LENGTH} characters.`;
25
+ }
26
+ if (password.length > PASSWORD_MAX_LENGTH) {
27
+ return `Password must be no more than ${PASSWORD_MAX_LENGTH} characters.`;
28
+ }
29
+ return null;
30
+ }
31
+ //# sourceMappingURL=password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/utils/password.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAEzF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,IAAI,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC1C,OAAO,6BAA6B,mBAAmB,cAAc,CAAC;IACxE,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC1C,OAAO,iCAAiC,mBAAmB,cAAc,CAAC;IAC5E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import { PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH } from '../constants/business-user.js';\n\n/**\n * The one TTT account-password contract, shared by registration, password reset,\n * and any future password surface. Length only — TTT enforces no composition\n * requirements, so spaces, printable Unicode, and emoji are all allowed.\n *\n * Deliberately NOT named `validatePassword`: Firebase's own SDK exports a\n * `validatePassword(auth, password)` that reads mutable hosted project policy.\n * This helper is the stable product contract; the two must never be confused.\n *\n * The value is never trimmed, normalized, or otherwise mutated, and confirmation\n * matching is the caller's job. Length is JavaScript string length (UTF-16 code\n * units), matching HTML input length behavior and the Firebase SDK — a\n * surrogate-pair emoji therefore counts as 2.\n *\n * Client validation is UX; the Firebase-enforced password policy remains the\n * security boundary (ENG-006).\n *\n * @returns `null` when the password is acceptable (both boundaries inclusive),\n * otherwise the user-facing reason it was rejected.\n */\nexport function validateTttPassword(password: string): string | null {\n if (password.length < PASSWORD_MIN_LENGTH) {\n return `Password must be at least ${PASSWORD_MIN_LENGTH} characters.`;\n }\n if (password.length > PASSWORD_MAX_LENGTH) {\n return `Password must be no more than ${PASSWORD_MAX_LENGTH} characters.`;\n }\n return null;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/ttt-core",
3
- "version": "0.79.21",
3
+ "version": "0.79.23",
4
4
  "description": "Core types, Firestore path constants, and shared constants for TTT Productions",
5
5
  "repository": {
6
6
  "type": "git",