@agenticmail/core 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1583,6 +1583,254 @@ declare function flushTelemetry(): void;
1583
1583
  declare function debug(tag: string, message: string): void;
1584
1584
  declare function debugWarn(tag: string, message: string): void;
1585
1585
 
1586
+ /**
1587
+ * Path-traversal safe filesystem joiner.
1588
+ *
1589
+ * # Why this exists
1590
+ *
1591
+ * Every host-integration installer writes files into a directory
1592
+ * derived from operator config / env vars (`CODEX_HOME`,
1593
+ * `CLAUDE_CODE_AGENTS_DIR`, etc) AND builds filenames from values
1594
+ * returned by the AgenticMail API (account names, agent metadata).
1595
+ * Both inputs cross trust boundaries:
1596
+ *
1597
+ * - The operator's env vars are mostly trusted — but the operator
1598
+ * could fat-finger a relative path that resolves outside the
1599
+ * intended dir, or paste a path containing `..` segments.
1600
+ * - The AgenticMail API responses cross the master-key boundary.
1601
+ * A compromised MCP session or stolen master key could create an
1602
+ * account whose `name` contains `../../etc/something` to escape
1603
+ * the agents directory at install time.
1604
+ *
1605
+ * The existing `sanitizeSubagentName` helpers filter most malicious
1606
+ * names, but defence-in-depth dictates a second boundary check at
1607
+ * the actual `fs.writeFile` call. This module is that second check.
1608
+ *
1609
+ * # API
1610
+ *
1611
+ * `safeJoin(baseDir, ...parts)` resolves the parts under `baseDir`
1612
+ * and throws `PathTraversalError` if the resulting absolute path
1613
+ * escapes `baseDir`. Use it instead of `path.join(baseDir, ...)` in
1614
+ * every code path that mixes operator config + user-provided
1615
+ * filenames.
1616
+ *
1617
+ * # Why this idiom (resolve + boundary check)
1618
+ *
1619
+ * - `path.resolve` normalises `..` segments deterministically.
1620
+ * - The startsWith check rejects any normalised path that climbed
1621
+ * out of `baseDir`.
1622
+ * - CodeQL's `js/path-injection` query recognises this exact shape
1623
+ * as a sanitizer, so the static-analysis warning is resolved at
1624
+ * the same time the runtime risk is closed.
1625
+ *
1626
+ * # Edge cases handled
1627
+ *
1628
+ * - `parts` containing `..` segments → throws.
1629
+ * - `parts` containing an absolute path → the absolute path wins
1630
+ * over `baseDir` per POSIX rules, which is almost always wrong
1631
+ * when the caller meant "filename inside baseDir". We reject
1632
+ * absolute segments unless `allowAbsolute: true` is set.
1633
+ * - `baseDir` itself is not normalised before comparison: pass an
1634
+ * already-resolved absolute path. The helper asserts this.
1635
+ * - Symlinks are NOT resolved. If you need symlink-safe joining,
1636
+ * wrap with `fs.realpath` separately. For our installers we
1637
+ * don't follow symlinks at all (we always do explicit
1638
+ * `existsSync` + `writeFileSync`), so this is fine.
1639
+ *
1640
+ * # Examples
1641
+ *
1642
+ * ```ts
1643
+ * // Safe: resolves to /home/ope/.codex/agents/agenticmail-fola.toml
1644
+ * safeJoin('/home/ope/.codex/agents', 'agenticmail-fola.toml');
1645
+ *
1646
+ * // Throws: '../etc/passwd' resolves outside the base dir
1647
+ * safeJoin('/home/ope/.codex/agents', '../etc/passwd');
1648
+ *
1649
+ * // Throws: an absolute path bypasses the base dir
1650
+ * safeJoin('/home/ope/.codex/agents', '/etc/passwd');
1651
+ * ```
1652
+ */
1653
+
1654
+ /**
1655
+ * Thrown when a `safeJoin` call would resolve to a path outside the
1656
+ * provided base directory. Carries the offending inputs for logging
1657
+ * (with the resolved path elided so we don't leak filesystem layout
1658
+ * to a remote attacker via error messages).
1659
+ */
1660
+ declare class PathTraversalError extends Error {
1661
+ readonly baseDir: string;
1662
+ readonly parts: string[];
1663
+ constructor(baseDir: string, parts: string[]);
1664
+ }
1665
+ interface SafeJoinOptions {
1666
+ /**
1667
+ * Allow segments that are themselves absolute paths. Off by default
1668
+ * because passing an absolute path to `path.join` discards the
1669
+ * base directory — almost always a bug at our call sites.
1670
+ */
1671
+ allowAbsolute?: boolean;
1672
+ }
1673
+ /**
1674
+ * Resolve `parts` under `baseDir`, asserting the result stays inside
1675
+ * `baseDir`. See module docstring for the rationale.
1676
+ *
1677
+ * @throws `PathTraversalError` if any segment is absolute (and
1678
+ * `allowAbsolute` is not set), or if the resolved path escapes
1679
+ * `baseDir`.
1680
+ */
1681
+ declare function safeJoin(baseDir: string, ...partsAndOpts: (string | SafeJoinOptions)[]): string;
1682
+ /**
1683
+ * Convenience: same as `safeJoin` but returns `null` instead of
1684
+ * throwing on a traversal attempt. Use in code paths where a single
1685
+ * malicious filename should be skipped, not propagated up as an
1686
+ * exception (e.g. iterating `readdirSync` results during cleanup).
1687
+ */
1688
+ declare function tryJoin(baseDir: string, ...parts: string[]): string | null;
1689
+ /**
1690
+ * Validate that a candidate path is already absolute and stays inside
1691
+ * `baseDir`. Use when receiving a path from external config that the
1692
+ * caller wants to treat as already-canonical (e.g. an env var that
1693
+ * names a project directory) — the helper asserts the safety
1694
+ * properties without further joining.
1695
+ */
1696
+ declare function assertWithinBase(baseDir: string, candidate: string): string;
1697
+
1698
+ /**
1699
+ * Secret-redaction helpers for log lines and diagnostic output.
1700
+ *
1701
+ * # Why this exists
1702
+ *
1703
+ * AgenticMail keeps three kinds of long-lived secrets in memory:
1704
+ *
1705
+ * 1. The master key (`mk_…`) — full admin scope on the local API.
1706
+ * 2. Per-agent API keys (`ak_…`) — scoped to one agent's mailbox.
1707
+ * 3. Stalwart's admin password and per-agent IMAP/SMTP passwords.
1708
+ *
1709
+ * All three flow through config objects, install results, and the
1710
+ * MCP-server env block. Several of those objects get logged for
1711
+ * diagnostic purposes (install completion summary, dispatcher
1712
+ * startup, error stacks). CodeQL's `js/clear-text-logging` query
1713
+ * flagged eight call sites where a secret could plausibly reach a
1714
+ * log line. This module is the canonical sanitizer for those spots.
1715
+ *
1716
+ * # API
1717
+ *
1718
+ * - `redactSecret(value)` collapses a string secret to a
1719
+ * fixed-shape redaction marker (`mk_***`, `ak_***`, `***`)
1720
+ * while preserving the prefix so the log reader can still tell
1721
+ * the kind of secret it was.
1722
+ * - `redactObject(obj)` walks an object and replaces every value
1723
+ * under a "sensitive-looking" key with REDACTED.
1724
+ * - `REDACTED` constant for cases where the caller wants to
1725
+ * compose their own log line.
1726
+ *
1727
+ * # Conservative match
1728
+ *
1729
+ * The "sensitive-looking" key detection is deliberately broad —
1730
+ * it's better to over-redact a harmless `name` field that happens
1731
+ * to contain "key" than to leak a secret. The currently-matched
1732
+ * key names (case-insensitive substring): `key`, `secret`,
1733
+ * `password`, `token`, `apikey`, `masterkey`, `authorization`,
1734
+ * `bearer`.
1735
+ */
1736
+ /** Returned in place of any redacted secret. */
1737
+ declare const REDACTED = "***";
1738
+ /**
1739
+ * Redact a string secret to a fixed-shape marker. Preserves the
1740
+ * known prefixes (`mk_`, `ak_`) so log readers can still tell what
1741
+ * kind of secret was elided.
1742
+ *
1743
+ * Non-string inputs return REDACTED unchanged.
1744
+ */
1745
+ declare function redactSecret(value: unknown): string;
1746
+ /**
1747
+ * Walk an object recursively. Any value under a key that looks
1748
+ * sensitive (see SENSITIVE_KEY_PATTERNS above) is replaced with
1749
+ * `REDACTED`. Arrays are walked too. The original object is NOT
1750
+ * mutated — returns a shallow-cloned tree with the same shape.
1751
+ *
1752
+ * Use this when you want to pass a whole config object into a log
1753
+ * line via JSON.stringify, e.g.:
1754
+ *
1755
+ * ```ts
1756
+ * log.info('install complete', redactObject(result));
1757
+ * ```
1758
+ *
1759
+ * Edge cases:
1760
+ * - Cycles are NOT supported; pass tree-shaped data only.
1761
+ * - Non-plain objects (Map, Set, Buffer, Date, Error) are returned
1762
+ * by reference unchanged — redaction is for plain config dicts.
1763
+ * - Symbols are skipped.
1764
+ */
1765
+ declare function redactObject<T>(input: T, _depth?: number): T;
1766
+
1767
+ /**
1768
+ * SSRF-safe URL validation for the AgenticMail API base URL.
1769
+ *
1770
+ * # What this is for
1771
+ *
1772
+ * Every host integration (claudecode, codex, …) reads the master API
1773
+ * URL from `~/.agenticmail/config.json` and uses it to build fetch
1774
+ * requests. CodeQL's `js/request-forgery` query flags those fetch
1775
+ * calls because the URL is operator-controlled (via the config file
1776
+ * or env vars) and could theoretically be redirected to an internal
1777
+ * service the host process can reach but the operator didn't intend.
1778
+ *
1779
+ * In practice the operator controls their own config file — there's
1780
+ * no remote attacker controlling apiUrl in a self-hosted install.
1781
+ * But defence-in-depth dictates we still validate, because:
1782
+ *
1783
+ * - A malicious npm package that planted an env var (e.g.
1784
+ * `AGENTICMAIL_API_URL=http://169.254.169.254/latest/meta-data/`)
1785
+ * could redirect the dispatcher's API calls to a cloud-metadata
1786
+ * endpoint and exfiltrate IAM credentials in the response body.
1787
+ * - A `file://` or `javascript:` scheme would let the operator's
1788
+ * own typo cause undefined behavior.
1789
+ *
1790
+ * # The check
1791
+ *
1792
+ * `validateApiUrl(url)` rejects:
1793
+ *
1794
+ * - Non-`http(s)://` schemes (`file://`, `javascript:`, `data:`,
1795
+ * `ftp://`, etc).
1796
+ * - Cloud metadata IPs (169.254.169.254 — AWS/Azure/GCP) and the
1797
+ * IPv6 equivalent fd00:ec2::254.
1798
+ * - Empty / malformed URLs (via `new URL` parse).
1799
+ *
1800
+ * The check intentionally does NOT restrict to localhost — operators
1801
+ * legitimately run AgenticMail on a NAS or remote VM and point the
1802
+ * host-integration installs at it. The cloud-metadata IPs are the
1803
+ * only blanket block.
1804
+ *
1805
+ * # Why a separate canonicalisation step
1806
+ *
1807
+ * Returning the URL via `url.origin` (rather than the raw input
1808
+ * string) gives CodeQL a sanitiser shape it recognises: the value
1809
+ * is now constrained to whatever `URL` parsed it into, with no
1810
+ * embedded credentials, query strings, or path traversal.
1811
+ */
1812
+ /** Thrown when `validateApiUrl` rejects a candidate URL. */
1813
+ declare class UnsafeApiUrlError extends Error {
1814
+ readonly raw: string;
1815
+ readonly reason: string;
1816
+ constructor(raw: string, reason: string);
1817
+ }
1818
+ /**
1819
+ * Validate the operator-supplied AgenticMail API base URL.
1820
+ *
1821
+ * Returns the canonical origin form of the URL (`http://host:port`)
1822
+ * so callers can build paths against it without worrying about
1823
+ * trailing slashes or embedded auth. Throws `UnsafeApiUrlError` on
1824
+ * any structural problem or blocked host.
1825
+ */
1826
+ declare function validateApiUrl(raw: unknown): string;
1827
+ /**
1828
+ * Build a full request URL from a validated base + path. Used by the
1829
+ * host-integration api clients to ensure the path is appended via
1830
+ * `URL` (escapes correctly) rather than string concat.
1831
+ */
1832
+ declare function buildApiUrl(baseOrigin: string, pathAndQuery: string): string;
1833
+
1586
1834
  interface DependencyStatus {
1587
1835
  name: string;
1588
1836
  installed: boolean;
@@ -2123,4 +2371,4 @@ declare class AgentMemoryStore {
2123
2371
  renderForPrompt(memory: AgentMemoryRead | null): string;
2124
2372
  }
2125
2373
 
2126
- export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryFields, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, type PurchasedDomain, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, WARNING_THRESHOLD, type WatcherOptions, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizeAddress, normalizePhoneNumber, normalizeSubject, parseEmail, parseGoogleVoiceSms, recordToolCall, resolveConfig, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge, threadIdFor };
2374
+ export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryFields, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, PathTraversalError, type PurchasedDomain, REDACTED, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, UnsafeApiUrlError, WARNING_THRESHOLD, type WatcherOptions, assertWithinBase, buildApiUrl, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizeAddress, normalizePhoneNumber, normalizeSubject, parseEmail, parseGoogleVoiceSms, recordToolCall, redactObject, redactSecret, resolveConfig, safeJoin, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge, threadIdFor, tryJoin, validateApiUrl };
package/dist/index.d.ts CHANGED
@@ -1583,6 +1583,254 @@ declare function flushTelemetry(): void;
1583
1583
  declare function debug(tag: string, message: string): void;
1584
1584
  declare function debugWarn(tag: string, message: string): void;
1585
1585
 
1586
+ /**
1587
+ * Path-traversal safe filesystem joiner.
1588
+ *
1589
+ * # Why this exists
1590
+ *
1591
+ * Every host-integration installer writes files into a directory
1592
+ * derived from operator config / env vars (`CODEX_HOME`,
1593
+ * `CLAUDE_CODE_AGENTS_DIR`, etc) AND builds filenames from values
1594
+ * returned by the AgenticMail API (account names, agent metadata).
1595
+ * Both inputs cross trust boundaries:
1596
+ *
1597
+ * - The operator's env vars are mostly trusted — but the operator
1598
+ * could fat-finger a relative path that resolves outside the
1599
+ * intended dir, or paste a path containing `..` segments.
1600
+ * - The AgenticMail API responses cross the master-key boundary.
1601
+ * A compromised MCP session or stolen master key could create an
1602
+ * account whose `name` contains `../../etc/something` to escape
1603
+ * the agents directory at install time.
1604
+ *
1605
+ * The existing `sanitizeSubagentName` helpers filter most malicious
1606
+ * names, but defence-in-depth dictates a second boundary check at
1607
+ * the actual `fs.writeFile` call. This module is that second check.
1608
+ *
1609
+ * # API
1610
+ *
1611
+ * `safeJoin(baseDir, ...parts)` resolves the parts under `baseDir`
1612
+ * and throws `PathTraversalError` if the resulting absolute path
1613
+ * escapes `baseDir`. Use it instead of `path.join(baseDir, ...)` in
1614
+ * every code path that mixes operator config + user-provided
1615
+ * filenames.
1616
+ *
1617
+ * # Why this idiom (resolve + boundary check)
1618
+ *
1619
+ * - `path.resolve` normalises `..` segments deterministically.
1620
+ * - The startsWith check rejects any normalised path that climbed
1621
+ * out of `baseDir`.
1622
+ * - CodeQL's `js/path-injection` query recognises this exact shape
1623
+ * as a sanitizer, so the static-analysis warning is resolved at
1624
+ * the same time the runtime risk is closed.
1625
+ *
1626
+ * # Edge cases handled
1627
+ *
1628
+ * - `parts` containing `..` segments → throws.
1629
+ * - `parts` containing an absolute path → the absolute path wins
1630
+ * over `baseDir` per POSIX rules, which is almost always wrong
1631
+ * when the caller meant "filename inside baseDir". We reject
1632
+ * absolute segments unless `allowAbsolute: true` is set.
1633
+ * - `baseDir` itself is not normalised before comparison: pass an
1634
+ * already-resolved absolute path. The helper asserts this.
1635
+ * - Symlinks are NOT resolved. If you need symlink-safe joining,
1636
+ * wrap with `fs.realpath` separately. For our installers we
1637
+ * don't follow symlinks at all (we always do explicit
1638
+ * `existsSync` + `writeFileSync`), so this is fine.
1639
+ *
1640
+ * # Examples
1641
+ *
1642
+ * ```ts
1643
+ * // Safe: resolves to /home/ope/.codex/agents/agenticmail-fola.toml
1644
+ * safeJoin('/home/ope/.codex/agents', 'agenticmail-fola.toml');
1645
+ *
1646
+ * // Throws: '../etc/passwd' resolves outside the base dir
1647
+ * safeJoin('/home/ope/.codex/agents', '../etc/passwd');
1648
+ *
1649
+ * // Throws: an absolute path bypasses the base dir
1650
+ * safeJoin('/home/ope/.codex/agents', '/etc/passwd');
1651
+ * ```
1652
+ */
1653
+
1654
+ /**
1655
+ * Thrown when a `safeJoin` call would resolve to a path outside the
1656
+ * provided base directory. Carries the offending inputs for logging
1657
+ * (with the resolved path elided so we don't leak filesystem layout
1658
+ * to a remote attacker via error messages).
1659
+ */
1660
+ declare class PathTraversalError extends Error {
1661
+ readonly baseDir: string;
1662
+ readonly parts: string[];
1663
+ constructor(baseDir: string, parts: string[]);
1664
+ }
1665
+ interface SafeJoinOptions {
1666
+ /**
1667
+ * Allow segments that are themselves absolute paths. Off by default
1668
+ * because passing an absolute path to `path.join` discards the
1669
+ * base directory — almost always a bug at our call sites.
1670
+ */
1671
+ allowAbsolute?: boolean;
1672
+ }
1673
+ /**
1674
+ * Resolve `parts` under `baseDir`, asserting the result stays inside
1675
+ * `baseDir`. See module docstring for the rationale.
1676
+ *
1677
+ * @throws `PathTraversalError` if any segment is absolute (and
1678
+ * `allowAbsolute` is not set), or if the resolved path escapes
1679
+ * `baseDir`.
1680
+ */
1681
+ declare function safeJoin(baseDir: string, ...partsAndOpts: (string | SafeJoinOptions)[]): string;
1682
+ /**
1683
+ * Convenience: same as `safeJoin` but returns `null` instead of
1684
+ * throwing on a traversal attempt. Use in code paths where a single
1685
+ * malicious filename should be skipped, not propagated up as an
1686
+ * exception (e.g. iterating `readdirSync` results during cleanup).
1687
+ */
1688
+ declare function tryJoin(baseDir: string, ...parts: string[]): string | null;
1689
+ /**
1690
+ * Validate that a candidate path is already absolute and stays inside
1691
+ * `baseDir`. Use when receiving a path from external config that the
1692
+ * caller wants to treat as already-canonical (e.g. an env var that
1693
+ * names a project directory) — the helper asserts the safety
1694
+ * properties without further joining.
1695
+ */
1696
+ declare function assertWithinBase(baseDir: string, candidate: string): string;
1697
+
1698
+ /**
1699
+ * Secret-redaction helpers for log lines and diagnostic output.
1700
+ *
1701
+ * # Why this exists
1702
+ *
1703
+ * AgenticMail keeps three kinds of long-lived secrets in memory:
1704
+ *
1705
+ * 1. The master key (`mk_…`) — full admin scope on the local API.
1706
+ * 2. Per-agent API keys (`ak_…`) — scoped to one agent's mailbox.
1707
+ * 3. Stalwart's admin password and per-agent IMAP/SMTP passwords.
1708
+ *
1709
+ * All three flow through config objects, install results, and the
1710
+ * MCP-server env block. Several of those objects get logged for
1711
+ * diagnostic purposes (install completion summary, dispatcher
1712
+ * startup, error stacks). CodeQL's `js/clear-text-logging` query
1713
+ * flagged eight call sites where a secret could plausibly reach a
1714
+ * log line. This module is the canonical sanitizer for those spots.
1715
+ *
1716
+ * # API
1717
+ *
1718
+ * - `redactSecret(value)` collapses a string secret to a
1719
+ * fixed-shape redaction marker (`mk_***`, `ak_***`, `***`)
1720
+ * while preserving the prefix so the log reader can still tell
1721
+ * the kind of secret it was.
1722
+ * - `redactObject(obj)` walks an object and replaces every value
1723
+ * under a "sensitive-looking" key with REDACTED.
1724
+ * - `REDACTED` constant for cases where the caller wants to
1725
+ * compose their own log line.
1726
+ *
1727
+ * # Conservative match
1728
+ *
1729
+ * The "sensitive-looking" key detection is deliberately broad —
1730
+ * it's better to over-redact a harmless `name` field that happens
1731
+ * to contain "key" than to leak a secret. The currently-matched
1732
+ * key names (case-insensitive substring): `key`, `secret`,
1733
+ * `password`, `token`, `apikey`, `masterkey`, `authorization`,
1734
+ * `bearer`.
1735
+ */
1736
+ /** Returned in place of any redacted secret. */
1737
+ declare const REDACTED = "***";
1738
+ /**
1739
+ * Redact a string secret to a fixed-shape marker. Preserves the
1740
+ * known prefixes (`mk_`, `ak_`) so log readers can still tell what
1741
+ * kind of secret was elided.
1742
+ *
1743
+ * Non-string inputs return REDACTED unchanged.
1744
+ */
1745
+ declare function redactSecret(value: unknown): string;
1746
+ /**
1747
+ * Walk an object recursively. Any value under a key that looks
1748
+ * sensitive (see SENSITIVE_KEY_PATTERNS above) is replaced with
1749
+ * `REDACTED`. Arrays are walked too. The original object is NOT
1750
+ * mutated — returns a shallow-cloned tree with the same shape.
1751
+ *
1752
+ * Use this when you want to pass a whole config object into a log
1753
+ * line via JSON.stringify, e.g.:
1754
+ *
1755
+ * ```ts
1756
+ * log.info('install complete', redactObject(result));
1757
+ * ```
1758
+ *
1759
+ * Edge cases:
1760
+ * - Cycles are NOT supported; pass tree-shaped data only.
1761
+ * - Non-plain objects (Map, Set, Buffer, Date, Error) are returned
1762
+ * by reference unchanged — redaction is for plain config dicts.
1763
+ * - Symbols are skipped.
1764
+ */
1765
+ declare function redactObject<T>(input: T, _depth?: number): T;
1766
+
1767
+ /**
1768
+ * SSRF-safe URL validation for the AgenticMail API base URL.
1769
+ *
1770
+ * # What this is for
1771
+ *
1772
+ * Every host integration (claudecode, codex, …) reads the master API
1773
+ * URL from `~/.agenticmail/config.json` and uses it to build fetch
1774
+ * requests. CodeQL's `js/request-forgery` query flags those fetch
1775
+ * calls because the URL is operator-controlled (via the config file
1776
+ * or env vars) and could theoretically be redirected to an internal
1777
+ * service the host process can reach but the operator didn't intend.
1778
+ *
1779
+ * In practice the operator controls their own config file — there's
1780
+ * no remote attacker controlling apiUrl in a self-hosted install.
1781
+ * But defence-in-depth dictates we still validate, because:
1782
+ *
1783
+ * - A malicious npm package that planted an env var (e.g.
1784
+ * `AGENTICMAIL_API_URL=http://169.254.169.254/latest/meta-data/`)
1785
+ * could redirect the dispatcher's API calls to a cloud-metadata
1786
+ * endpoint and exfiltrate IAM credentials in the response body.
1787
+ * - A `file://` or `javascript:` scheme would let the operator's
1788
+ * own typo cause undefined behavior.
1789
+ *
1790
+ * # The check
1791
+ *
1792
+ * `validateApiUrl(url)` rejects:
1793
+ *
1794
+ * - Non-`http(s)://` schemes (`file://`, `javascript:`, `data:`,
1795
+ * `ftp://`, etc).
1796
+ * - Cloud metadata IPs (169.254.169.254 — AWS/Azure/GCP) and the
1797
+ * IPv6 equivalent fd00:ec2::254.
1798
+ * - Empty / malformed URLs (via `new URL` parse).
1799
+ *
1800
+ * The check intentionally does NOT restrict to localhost — operators
1801
+ * legitimately run AgenticMail on a NAS or remote VM and point the
1802
+ * host-integration installs at it. The cloud-metadata IPs are the
1803
+ * only blanket block.
1804
+ *
1805
+ * # Why a separate canonicalisation step
1806
+ *
1807
+ * Returning the URL via `url.origin` (rather than the raw input
1808
+ * string) gives CodeQL a sanitiser shape it recognises: the value
1809
+ * is now constrained to whatever `URL` parsed it into, with no
1810
+ * embedded credentials, query strings, or path traversal.
1811
+ */
1812
+ /** Thrown when `validateApiUrl` rejects a candidate URL. */
1813
+ declare class UnsafeApiUrlError extends Error {
1814
+ readonly raw: string;
1815
+ readonly reason: string;
1816
+ constructor(raw: string, reason: string);
1817
+ }
1818
+ /**
1819
+ * Validate the operator-supplied AgenticMail API base URL.
1820
+ *
1821
+ * Returns the canonical origin form of the URL (`http://host:port`)
1822
+ * so callers can build paths against it without worrying about
1823
+ * trailing slashes or embedded auth. Throws `UnsafeApiUrlError` on
1824
+ * any structural problem or blocked host.
1825
+ */
1826
+ declare function validateApiUrl(raw: unknown): string;
1827
+ /**
1828
+ * Build a full request URL from a validated base + path. Used by the
1829
+ * host-integration api clients to ensure the path is appended via
1830
+ * `URL` (escapes correctly) rather than string concat.
1831
+ */
1832
+ declare function buildApiUrl(baseOrigin: string, pathAndQuery: string): string;
1833
+
1586
1834
  interface DependencyStatus {
1587
1835
  name: string;
1588
1836
  installed: boolean;
@@ -2123,4 +2371,4 @@ declare class AgentMemoryStore {
2123
2371
  renderForPrompt(memory: AgentMemoryRead | null): string;
2124
2372
  }
2125
2373
 
2126
- export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryFields, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, type PurchasedDomain, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, WARNING_THRESHOLD, type WatcherOptions, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizeAddress, normalizePhoneNumber, normalizeSubject, parseEmail, parseGoogleVoiceSms, recordToolCall, resolveConfig, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge, threadIdFor };
2374
+ export { AGENT_ROLES, AccountManager, type AddressInfo, type Agent, AgentDeletionService, type AgentMemoryFields, type AgentMemoryOptions, type AgentMemoryRead, AgentMemoryStore, type AgentRole, AgenticMailClient, type AgenticMailClientOptions, type AgenticMailConfig, type ArchiveAndDeleteOptions, type ArchivedEmail, type Attachment, type AttachmentAdvisory, type CachedMessage, CloudflareClient, type CreateAgentOptions, DEFAULT_AGENT_NAME, DEFAULT_AGENT_ROLE, DNSConfigurator, type Database, type DeletionReport, type DeletionSummary, DependencyChecker, DependencyInstaller, type DependencyStatus, type DnsRecord, type DnsSetupResult, type DomainInfo, DomainManager, type DomainModeConfig, type DomainPurchaseResult, DomainPurchaser, type DomainSearchResult, type DomainSetupResult, type EmailEnvelope, type EmailRouteAction, type EmailRouteClass, type EmailRouteClassification, type EmailRouteInput, EmailSearchIndex, type FolderInfo, type GatewayConfig, GatewayManager, type GatewayManagerOptions, type GatewayMode, type GatewayStatus, type InboundEmail, type InboxEvent, type InboxExpungeEvent, type InboxFlagsEvent, type InboxNewEvent, InboxWatcher, type InboxWatcherOptions, type InstallProgress, type LinkAdvisory, type LocalSmtpConfig, MailReceiver, type MailReceiverOptions, MailSender, type MailSenderOptions, type MailboxInfo, type OutboundCategory, type OutboundScanInput, type OutboundScanResult, type OutboundWarning, type ParsedAttachment, type ParsedEmail, type ParsedSms, PathTraversalError, type PurchasedDomain, REDACTED, RELAY_PRESETS, RelayBridge, type RelayBridgeOptions, type RelayConfig, RelayGateway, type RelayProvider, type RelaySearchResult, SPAM_THRESHOLD, type SafeJoinOptions, type SanitizeDetection, type SanitizeResult, type SearchCriteria, type SearchableEmail, type SecurityAdvisory, type SendMailOptions, type SendResult, type SendResultWithRaw, ServiceManager, type ServiceStatus, type SetupConfig, SetupManager, type SetupResult, type Severity, type SmsConfig, SmsManager, type SmsMessage, SmsPoller, type SpamCategory, type SpamResult, type SpamRuleMatch, StalwartAdmin, type StalwartAdminOptions, type StalwartPrincipal, ThreadCache, type ThreadCacheEntry, type ThreadCacheOptions, type ThreadIdInput, type TunnelConfig, TunnelManager, UnsafeApiUrlError, WARNING_THRESHOLD, type WatcherOptions, assertWithinBase, buildApiUrl, buildInboundSecurityAdvisory, classifyEmailRoute, closeDatabase, createTestDatabase, debug, debugWarn, ensureDataDir, extractVerificationCode, flushTelemetry, getDatabase, isInternalEmail, isValidPhoneNumber, normalizeAddress, normalizePhoneNumber, normalizeSubject, parseEmail, parseGoogleVoiceSms, recordToolCall, redactObject, redactSecret, resolveConfig, safeJoin, sanitizeEmail, saveConfig, scanOutboundEmail, scoreEmail, setTelemetryVersion, startRelayBridge, threadIdFor, tryJoin, validateApiUrl };