agent-inspect 6.31.3 → 6.31.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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/SECURITY.md +2 -0
- package/docs/GETTING-STARTED.md +2 -0
- package/docs/SAFE-TRACE-SHARING.md +1 -0
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-SJ6FRNOE.mjs → chunk-LKEPABND.mjs} +86 -80
- package/packages/cli/dist/chunk-LKEPABND.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +449 -91
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +357 -14
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-YNEJOPRA.mjs → src-4WYF5RFI.mjs} +3 -3
- package/packages/cli/dist/{src-YNEJOPRA.mjs.map → src-4WYF5RFI.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +97 -30
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.mjs +13 -13
- package/packages/core/dist/checks.cjs +51 -49
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.mjs +5 -5
- package/packages/core/dist/{chunk-PBNBQRXV.mjs → chunk-2KJR6X4V.mjs} +9 -9
- package/packages/core/dist/chunk-2KJR6X4V.mjs.map +1 -0
- package/packages/core/dist/{chunk-26YJSETN.mjs → chunk-7DHSWDKB.mjs} +19 -6
- package/packages/core/dist/chunk-7DHSWDKB.mjs.map +1 -0
- package/packages/core/dist/{chunk-BGF5JDJN.mjs → chunk-INE5IWLN.mjs} +3 -3
- package/packages/core/dist/{chunk-BGF5JDJN.mjs.map → chunk-INE5IWLN.mjs.map} +1 -1
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs → chunk-MTMTXTJL.mjs} +5 -5
- package/packages/core/dist/{chunk-ZFJ2ICTC.mjs.map → chunk-MTMTXTJL.mjs.map} +1 -1
- package/packages/core/dist/{chunk-KWBO36PH.mjs → chunk-NY4ILMXK.mjs} +4 -4
- package/packages/core/dist/{chunk-KWBO36PH.mjs.map → chunk-NY4ILMXK.mjs.map} +1 -1
- package/packages/core/dist/{chunk-WN5PT4GV.mjs → chunk-OSGNGRHF.mjs} +3 -3
- package/packages/core/dist/{chunk-WN5PT4GV.mjs.map → chunk-OSGNGRHF.mjs.map} +1 -1
- package/packages/core/dist/{chunk-CBPWLHNQ.mjs → chunk-PKDBFBJ7.mjs} +5 -76
- package/packages/core/dist/chunk-PKDBFBJ7.mjs.map +1 -0
- package/packages/core/dist/{chunk-VU6O5QAH.mjs → chunk-VLVCLAPB.mjs} +83 -5
- package/packages/core/dist/chunk-VLVCLAPB.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +242 -6
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +143 -9
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/index.cjs +97 -8
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.mjs +6 -6
- package/packages/core/dist/logs.cjs +80 -2
- package/packages/core/dist/logs.cjs.map +1 -1
- package/packages/core/dist/logs.d.cts +2 -0
- package/packages/core/dist/logs.d.ts +2 -0
- package/packages/core/dist/logs.mjs +2 -2
- package/packages/core/dist/readers.cjs +2 -0
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.mjs +2 -2
- package/packages/core/dist/writers.cjs +96 -8
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.mjs +7 -8
- package/packages/core/dist/writers.mjs.map +1 -1
- package/packages/cli/dist/chunk-SJ6FRNOE.mjs.map +0 -1
- package/packages/core/dist/chunk-26YJSETN.mjs.map +0 -1
- package/packages/core/dist/chunk-CBPWLHNQ.mjs.map +0 -1
- package/packages/core/dist/chunk-PBNBQRXV.mjs.map +0 -1
- package/packages/core/dist/chunk-VU6O5QAH.mjs.map +0 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, truncateStringForProfile, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, diffRuns, manualTraceEventsToComparableRun } from './chunk-
|
|
2
|
+
import { ATTRIBUTION_CONFIDENCES, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createSafetyOversizedAttributeRule, resolveTraceDir, parseDuration, buildSessionIndex, resolveBundleRunIds, getTraceFilePath, openTrace, sha256Hex, collectTraceSchemaVersions, aggregateBundleSafeStatus, bundleFailsOnSafety, bundleRunAssetRelativePath, buildPlaceholderArtifact, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceHtmlShell, buildEvidenceProvenanceViewHtml, EVIDENCE_FORMAT_VERSION, inferEvidenceFileRole, buildEvidenceSafetyViewHtml, buildEvidenceDiffViewHtml, buildEvidenceCircuitViewHtml, buildEvidenceOutcomesViewHtml, buildEvidenceContractsViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceCausalFailureViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceTreeViewHtml, buildEvidenceManifest, serializeEvidenceManifest, buildZipArchive, assertBundlePathContained, enrichSessionRunRecord, normalizeBundleOutputPath, sanitizeBundleRunId, defaultBundleOutputPath, runTraceChecks, assertEvidenceRelativePath, TraceDirectory, loadTraceMetadataList, loadSessionRunRecords, TraceReadError, resolveRedactionProfile, applyProfileMetadataCaps, Redactor, flattenTree, escapeHtml, safeString, compactAttributes, stableJson, escapeMarkdown, DEFAULT_REDACT_KEYS, truncateStringForProfile, stringContainsHighConfidenceCredential, isCredentialSensitiveKey, extractMetadata, filterTraces, truncateName, buildRunSummary, formatDuration, formatTimestamp, renderErrorLine, isAgentInspectTrace, defineTraceContract, filterMetasBySessionScope, evaluateTraceContract, aggregateSessionCheckResults, createBaselineRegressionRule, summarizeSemanticParity, buildEvidenceCiPackage, verifyEvidenceDirectory, diffTraceEvents, renderRunDiff, buildRunTimeline, renderTimeline, buildTraceStats, renderTraceStats, parseDurationFilter, searchTraces, formatStepLabel, buildActivitySummary, renderActivitySummaryHuman, buildRunWhatSummary, renderRunWhat, buildLocalExplanation, resolveSuiteTemplate, defaultSuiteConfigTemplate, loadSuiteConfig, validateSuiteConfig, runSuite, renderSuiteReport, analyzeCohort, renderCohortReport, gateHasThresholds, runGate, renderGateReport, persistedInspectEventsToTraceEvents, renderStepLine, getIndent, TreeBuilder, zeroKinds, validateEvent, traceEventToPersistedInspectEvent, isPersistedInspectEvent, createRunStatusRule, createStructureOrphanRule, createStructureCycleRule, createRunDurationRule, createMaxStepDurationRule, createRequireCompletedRule, createStallDetectionRule, createObservedOutcomeRule, createRunDepthRule, createToolUsageRule, createLlmUsageRule, createStructureRelationshipRule, createStructureParallelWidthRule, projectLogicalEvents, summarizeObservedOutcomes, extractOutcomesFromTraceEvents, renderObservedOutcomesMarkdown, renderObservedOutcomesHtml, DEFAULT_SUITE_ARTIFACTS_DIR, parseCohortMetricList, COHORT_METRIC_IDS, parseGateList, nanoid, isAttributionConfidence, diffRuns, manualTraceEventsToComparableRun } from './chunk-LKEPABND.mjs';
|
|
3
3
|
import { realpathSync, constants as constants$1, existsSync, readFileSync } from 'fs';
|
|
4
4
|
import path19 from 'path';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
6
6
|
import { Command, Option } from 'commander';
|
|
7
7
|
import { readFile, stat, mkdir, writeFile, unlink, appendFile, access, rm, open, readdir, lstat, constants } from 'fs/promises';
|
|
8
8
|
import process2, { stdin, stdout } from 'process';
|
|
9
|
+
import { StringDecoder } from 'string_decoder';
|
|
9
10
|
import crypto, { createHash } from 'crypto';
|
|
10
11
|
import { execFile, spawn } from 'child_process';
|
|
11
12
|
import { promisify } from 'util';
|
|
@@ -13,7 +14,7 @@ import { createServer } from 'http';
|
|
|
13
14
|
import { createRequire } from 'module';
|
|
14
15
|
|
|
15
16
|
// package.json
|
|
16
|
-
var version = "6.31.
|
|
17
|
+
var version = "6.31.5";
|
|
17
18
|
|
|
18
19
|
// packages/cli/src/trace-dir-scale.ts
|
|
19
20
|
var TRACE_COUNT_WARN = 1e3;
|
|
@@ -1576,6 +1577,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
1576
1577
|
await readFileOnce(filePath, onLine);
|
|
1577
1578
|
return;
|
|
1578
1579
|
}
|
|
1580
|
+
let decoder = new StringDecoder("utf-8");
|
|
1579
1581
|
pos = st.size;
|
|
1580
1582
|
while (!shouldStop()) {
|
|
1581
1583
|
let next;
|
|
@@ -1589,6 +1591,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
1589
1591
|
pos = 0;
|
|
1590
1592
|
carry = "";
|
|
1591
1593
|
lineNumber = 0;
|
|
1594
|
+
decoder = new StringDecoder("utf-8");
|
|
1592
1595
|
}
|
|
1593
1596
|
if (next.size > pos) {
|
|
1594
1597
|
const fh = await open(filePath, "r");
|
|
@@ -1597,7 +1600,7 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
1597
1600
|
const buf = Buffer.allocUnsafe(Number(len));
|
|
1598
1601
|
const { bytesRead } = await fh.read(buf, 0, buf.length, pos);
|
|
1599
1602
|
pos += bytesRead;
|
|
1600
|
-
const chunk = carry + buf.
|
|
1603
|
+
const chunk = carry + decoder.write(buf.subarray(0, bytesRead));
|
|
1601
1604
|
const parts = chunk.split(/\r?\n/);
|
|
1602
1605
|
carry = parts.pop() ?? "";
|
|
1603
1606
|
for (const p of parts) {
|
|
@@ -1757,6 +1760,124 @@ async function tail(options = {}) {
|
|
|
1757
1760
|
// packages/core/src/exporters/types.ts
|
|
1758
1761
|
var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
1759
1762
|
|
|
1763
|
+
// packages/core/src/safety/url-redaction.ts
|
|
1764
|
+
var HTTP_URL = /^https?:\/\/[^\s]+$/i;
|
|
1765
|
+
var CONNECTION_PROTOCOLS = /* @__PURE__ */ new Set([
|
|
1766
|
+
"postgres:",
|
|
1767
|
+
"postgresql:",
|
|
1768
|
+
"mysql:",
|
|
1769
|
+
"mariadb:",
|
|
1770
|
+
"mongodb:",
|
|
1771
|
+
"mongodb+srv:",
|
|
1772
|
+
"redis:",
|
|
1773
|
+
"rediss:",
|
|
1774
|
+
"amqp:",
|
|
1775
|
+
"amqps:"
|
|
1776
|
+
]);
|
|
1777
|
+
var CONNECTION_URI = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
|
|
1778
|
+
var UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1779
|
+
var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
|
|
1780
|
+
var NUMERIC_SEGMENT = /^\d{5,}$/;
|
|
1781
|
+
var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
|
|
1782
|
+
function looksLikeHttpUrl(value) {
|
|
1783
|
+
return HTTP_URL.test(value.trim());
|
|
1784
|
+
}
|
|
1785
|
+
function looksLikeConnectionUri(value) {
|
|
1786
|
+
return CONNECTION_URI.test(value.trim());
|
|
1787
|
+
}
|
|
1788
|
+
function looksLikeUrlForRedaction(value) {
|
|
1789
|
+
return looksLikeHttpUrl(value) || looksLikeConnectionUri(value);
|
|
1790
|
+
}
|
|
1791
|
+
function parseRedactableUrl(value) {
|
|
1792
|
+
try {
|
|
1793
|
+
const url = new URL(value.trim());
|
|
1794
|
+
if (url.protocol === "http:" || url.protocol === "https:") return url;
|
|
1795
|
+
if (CONNECTION_PROTOCOLS.has(url.protocol)) return url;
|
|
1796
|
+
return void 0;
|
|
1797
|
+
} catch {
|
|
1798
|
+
return void 0;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
function isHttpProtocol(protocol) {
|
|
1802
|
+
return protocol === "http:" || protocol === "https:";
|
|
1803
|
+
}
|
|
1804
|
+
function isIdentifierSegment(segment) {
|
|
1805
|
+
if (UUID_SEGMENT.test(segment)) return true;
|
|
1806
|
+
if (HEX_SEGMENT.test(segment)) return true;
|
|
1807
|
+
if (NUMERIC_SEGMENT.test(segment)) return true;
|
|
1808
|
+
return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
1809
|
+
}
|
|
1810
|
+
function decodeParam(value) {
|
|
1811
|
+
try {
|
|
1812
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
1813
|
+
} catch {
|
|
1814
|
+
return value;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
function redactParams(raw, options, replacement) {
|
|
1818
|
+
let redacted = false;
|
|
1819
|
+
const parts = raw.split("&").map((part) => {
|
|
1820
|
+
if (part.length === 0) return part;
|
|
1821
|
+
const separator = part.indexOf("=");
|
|
1822
|
+
if (separator < 0) return part;
|
|
1823
|
+
const rawKey = part.slice(0, separator);
|
|
1824
|
+
const rawValue = part.slice(separator + 1);
|
|
1825
|
+
const key = decodeParam(rawKey);
|
|
1826
|
+
const value = decodeParam(rawValue);
|
|
1827
|
+
if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
1828
|
+
redacted = true;
|
|
1829
|
+
return `${rawKey}=${replacement}`;
|
|
1830
|
+
}
|
|
1831
|
+
return part;
|
|
1832
|
+
});
|
|
1833
|
+
return { value: parts.join("&"), redacted };
|
|
1834
|
+
}
|
|
1835
|
+
function redactUrlString(value, options) {
|
|
1836
|
+
const unchanged = {
|
|
1837
|
+
value,
|
|
1838
|
+
credentialRedacted: false,
|
|
1839
|
+
identifiersMasked: false
|
|
1840
|
+
};
|
|
1841
|
+
if (!looksLikeUrlForRedaction(value)) return unchanged;
|
|
1842
|
+
const url = parseRedactableUrl(value);
|
|
1843
|
+
if (!url) return unchanged;
|
|
1844
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
1845
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
1846
|
+
const http = isHttpProtocol(url.protocol);
|
|
1847
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
1848
|
+
let identifiersMasked = false;
|
|
1849
|
+
let pathname = url.pathname;
|
|
1850
|
+
if (http && options.maskPathIds === true) {
|
|
1851
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
1852
|
+
if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
|
|
1853
|
+
return segment;
|
|
1854
|
+
}
|
|
1855
|
+
identifiersMasked = true;
|
|
1856
|
+
return idPlaceholder;
|
|
1857
|
+
}).join("/");
|
|
1858
|
+
}
|
|
1859
|
+
let search = "";
|
|
1860
|
+
if (url.search.length > 1) {
|
|
1861
|
+
const params = redactParams(url.search.slice(1), options, replacement);
|
|
1862
|
+
if (params.redacted) credentialRedacted = true;
|
|
1863
|
+
search = `?${params.value}`;
|
|
1864
|
+
} else if (url.search.length === 1) {
|
|
1865
|
+
search = "?";
|
|
1866
|
+
}
|
|
1867
|
+
let hash = url.hash;
|
|
1868
|
+
if (url.hash.includes("=")) {
|
|
1869
|
+
const params = redactParams(url.hash.slice(1), options, replacement);
|
|
1870
|
+
if (params.redacted) credentialRedacted = true;
|
|
1871
|
+
hash = `#${params.value}`;
|
|
1872
|
+
}
|
|
1873
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
1874
|
+
return {
|
|
1875
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
1876
|
+
credentialRedacted,
|
|
1877
|
+
identifiersMasked
|
|
1878
|
+
};
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1760
1881
|
// packages/core/src/exporters/redact-export.ts
|
|
1761
1882
|
function isRecord4(value) {
|
|
1762
1883
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1774,21 +1895,34 @@ function deepClone(value) {
|
|
|
1774
1895
|
}
|
|
1775
1896
|
return out;
|
|
1776
1897
|
}
|
|
1777
|
-
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1898
|
+
function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1778
1899
|
if (depth > 32) {
|
|
1779
1900
|
return { truncated: true, reason: "maxDepth" };
|
|
1780
1901
|
}
|
|
1781
1902
|
const out = {};
|
|
1782
1903
|
for (const [key, value] of Object.entries(record)) {
|
|
1783
|
-
out[key] = boundValue(
|
|
1904
|
+
out[key] = boundValue(
|
|
1905
|
+
value,
|
|
1906
|
+
key,
|
|
1907
|
+
sensitiveKeys,
|
|
1908
|
+
maxMetadataValueLength,
|
|
1909
|
+
maxPreviewLength,
|
|
1910
|
+
seen,
|
|
1911
|
+
depth
|
|
1912
|
+
);
|
|
1784
1913
|
}
|
|
1785
1914
|
return out;
|
|
1786
1915
|
}
|
|
1787
|
-
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1916
|
+
function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1788
1917
|
if (value === null || typeof value !== "object") {
|
|
1789
1918
|
if (typeof value === "string") {
|
|
1919
|
+
const rewritten = redactUrlString(value, {
|
|
1920
|
+
sensitiveKeys,
|
|
1921
|
+
isSensitiveValue: stringContainsHighConfidenceCredential,
|
|
1922
|
+
maskPathIds: true
|
|
1923
|
+
}).value;
|
|
1790
1924
|
return truncateStringForProfile(
|
|
1791
|
-
|
|
1925
|
+
stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
|
|
1792
1926
|
key,
|
|
1793
1927
|
maxMetadataValueLength,
|
|
1794
1928
|
maxPreviewLength
|
|
@@ -1803,6 +1937,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
1803
1937
|
(item, index) => boundValue(
|
|
1804
1938
|
item,
|
|
1805
1939
|
String(index),
|
|
1940
|
+
sensitiveKeys,
|
|
1806
1941
|
maxMetadataValueLength,
|
|
1807
1942
|
maxPreviewLength,
|
|
1808
1943
|
seen,
|
|
@@ -1812,6 +1947,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
1812
1947
|
}
|
|
1813
1948
|
return boundAttributeValues(
|
|
1814
1949
|
value,
|
|
1950
|
+
sensitiveKeys,
|
|
1815
1951
|
maxMetadataValueLength,
|
|
1816
1952
|
maxPreviewLength,
|
|
1817
1953
|
seen,
|
|
@@ -1826,6 +1962,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
1826
1962
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
1827
1963
|
const bounded = boundAttributeValues(
|
|
1828
1964
|
redacted,
|
|
1965
|
+
[...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
|
|
1829
1966
|
maxMetadataValueLength,
|
|
1830
1967
|
maxPreviewLength,
|
|
1831
1968
|
seen,
|
|
@@ -3402,7 +3539,7 @@ async function newestTraceMtimeMs(traceDir) {
|
|
|
3402
3539
|
}
|
|
3403
3540
|
async function loadSessionRuns(traceDir) {
|
|
3404
3541
|
try {
|
|
3405
|
-
const mod = await import('./src-
|
|
3542
|
+
const mod = await import('./src-4WYF5RFI.mjs');
|
|
3406
3543
|
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
3407
3544
|
const status = mod.indexStatus(dbPath);
|
|
3408
3545
|
if (!status.healthy) {
|
|
@@ -3904,7 +4041,7 @@ function isTokenCredentialKey(normalized) {
|
|
|
3904
4041
|
if (normalized.endsWith("tokens")) return false;
|
|
3905
4042
|
return normalized.endsWith("token");
|
|
3906
4043
|
}
|
|
3907
|
-
function
|
|
4044
|
+
function isCredentialSensitiveKey2(key, sensitiveKeys) {
|
|
3908
4045
|
if (!key) return false;
|
|
3909
4046
|
const normalized = normalizeSensitiveKey(key);
|
|
3910
4047
|
if (!normalized) return false;
|
|
@@ -3925,7 +4062,7 @@ function isCredentialSensitiveKey(key, sensitiveKeys) {
|
|
|
3925
4062
|
}
|
|
3926
4063
|
|
|
3927
4064
|
// packages/redact/src/key-value-secret.ts
|
|
3928
|
-
var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})`;
|
|
4065
|
+
var KEY_VALUE_SECRET_PATTERN_SOURCE = String.raw`\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=(?!\[(?:REDACTED(?::[^\]]*)?|HASH:[0-9a-f]{8})\](?![^\s"'\\&#?;/]))([^\s"'\\]{8,})`;
|
|
3929
4066
|
var KEY_VALUE_SECRET_PATTERN = new RegExp(
|
|
3930
4067
|
KEY_VALUE_SECRET_PATTERN_SOURCE,
|
|
3931
4068
|
"i"
|
|
@@ -3935,6 +4072,124 @@ function valueContainsKeyValueSecret(value) {
|
|
|
3935
4072
|
return KEY_VALUE_SECRET_PATTERN.test(value);
|
|
3936
4073
|
}
|
|
3937
4074
|
|
|
4075
|
+
// packages/redact/src/url-redaction.ts
|
|
4076
|
+
var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
|
|
4077
|
+
var CONNECTION_PROTOCOLS2 = /* @__PURE__ */ new Set([
|
|
4078
|
+
"postgres:",
|
|
4079
|
+
"postgresql:",
|
|
4080
|
+
"mysql:",
|
|
4081
|
+
"mariadb:",
|
|
4082
|
+
"mongodb:",
|
|
4083
|
+
"mongodb+srv:",
|
|
4084
|
+
"redis:",
|
|
4085
|
+
"rediss:",
|
|
4086
|
+
"amqp:",
|
|
4087
|
+
"amqps:"
|
|
4088
|
+
]);
|
|
4089
|
+
var CONNECTION_URI2 = /^(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|rediss?|amqps?):\/\/[^\s]+$/i;
|
|
4090
|
+
var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4091
|
+
var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
|
|
4092
|
+
var NUMERIC_SEGMENT2 = /^\d{5,}$/;
|
|
4093
|
+
var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
|
|
4094
|
+
function looksLikeHttpUrl2(value) {
|
|
4095
|
+
return HTTP_URL2.test(value.trim());
|
|
4096
|
+
}
|
|
4097
|
+
function looksLikeConnectionUri2(value) {
|
|
4098
|
+
return CONNECTION_URI2.test(value.trim());
|
|
4099
|
+
}
|
|
4100
|
+
function looksLikeUrlForRedaction2(value) {
|
|
4101
|
+
return looksLikeHttpUrl2(value) || looksLikeConnectionUri2(value);
|
|
4102
|
+
}
|
|
4103
|
+
function parseRedactableUrl2(value) {
|
|
4104
|
+
try {
|
|
4105
|
+
const url = new URL(value.trim());
|
|
4106
|
+
if (url.protocol === "http:" || url.protocol === "https:") return url;
|
|
4107
|
+
if (CONNECTION_PROTOCOLS2.has(url.protocol)) return url;
|
|
4108
|
+
return void 0;
|
|
4109
|
+
} catch {
|
|
4110
|
+
return void 0;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
function isHttpProtocol2(protocol) {
|
|
4114
|
+
return protocol === "http:" || protocol === "https:";
|
|
4115
|
+
}
|
|
4116
|
+
function isIdentifierSegment2(segment) {
|
|
4117
|
+
if (UUID_SEGMENT2.test(segment)) return true;
|
|
4118
|
+
if (HEX_SEGMENT2.test(segment)) return true;
|
|
4119
|
+
if (NUMERIC_SEGMENT2.test(segment)) return true;
|
|
4120
|
+
return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
4121
|
+
}
|
|
4122
|
+
function decodeParam2(value) {
|
|
4123
|
+
try {
|
|
4124
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
4125
|
+
} catch {
|
|
4126
|
+
return value;
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
function redactParams2(raw, options, replacement) {
|
|
4130
|
+
let redacted = false;
|
|
4131
|
+
const parts = raw.split("&").map((part) => {
|
|
4132
|
+
if (part.length === 0) return part;
|
|
4133
|
+
const separator = part.indexOf("=");
|
|
4134
|
+
if (separator < 0) return part;
|
|
4135
|
+
const rawKey = part.slice(0, separator);
|
|
4136
|
+
const rawValue = part.slice(separator + 1);
|
|
4137
|
+
const key = decodeParam2(rawKey);
|
|
4138
|
+
const value = decodeParam2(rawValue);
|
|
4139
|
+
if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
4140
|
+
redacted = true;
|
|
4141
|
+
return `${rawKey}=${replacement}`;
|
|
4142
|
+
}
|
|
4143
|
+
return part;
|
|
4144
|
+
});
|
|
4145
|
+
return { value: parts.join("&"), redacted };
|
|
4146
|
+
}
|
|
4147
|
+
function redactUrlString2(value, options) {
|
|
4148
|
+
const unchanged = {
|
|
4149
|
+
value,
|
|
4150
|
+
credentialRedacted: false,
|
|
4151
|
+
identifiersMasked: false
|
|
4152
|
+
};
|
|
4153
|
+
if (!looksLikeUrlForRedaction2(value)) return unchanged;
|
|
4154
|
+
const url = parseRedactableUrl2(value);
|
|
4155
|
+
if (!url) return unchanged;
|
|
4156
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
4157
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
4158
|
+
const http = isHttpProtocol2(url.protocol);
|
|
4159
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
4160
|
+
let identifiersMasked = false;
|
|
4161
|
+
let pathname = url.pathname;
|
|
4162
|
+
if (http && options.maskPathIds === true) {
|
|
4163
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
4164
|
+
if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
|
|
4165
|
+
return segment;
|
|
4166
|
+
}
|
|
4167
|
+
identifiersMasked = true;
|
|
4168
|
+
return idPlaceholder;
|
|
4169
|
+
}).join("/");
|
|
4170
|
+
}
|
|
4171
|
+
let search = "";
|
|
4172
|
+
if (url.search.length > 1) {
|
|
4173
|
+
const params = redactParams2(url.search.slice(1), options, replacement);
|
|
4174
|
+
if (params.redacted) credentialRedacted = true;
|
|
4175
|
+
search = `?${params.value}`;
|
|
4176
|
+
} else if (url.search.length === 1) {
|
|
4177
|
+
search = "?";
|
|
4178
|
+
}
|
|
4179
|
+
let hash = url.hash;
|
|
4180
|
+
if (url.hash.includes("=")) {
|
|
4181
|
+
const params = redactParams2(url.hash.slice(1), options, replacement);
|
|
4182
|
+
if (params.redacted) credentialRedacted = true;
|
|
4183
|
+
hash = `#${params.value}`;
|
|
4184
|
+
}
|
|
4185
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
4186
|
+
return {
|
|
4187
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
4188
|
+
credentialRedacted,
|
|
4189
|
+
identifiersMasked
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
|
|
3938
4193
|
// packages/redact/src/index.ts
|
|
3939
4194
|
var DEFAULT_REDACT_KEYS2 = [
|
|
3940
4195
|
"authorization",
|
|
@@ -4001,7 +4256,9 @@ function findCompiledKeyRule(key, rules) {
|
|
|
4001
4256
|
const exact = toKey(key);
|
|
4002
4257
|
const direct = rules.find((candidate) => candidate.key === exact);
|
|
4003
4258
|
if (direct) return direct;
|
|
4004
|
-
return rules.find(
|
|
4259
|
+
return rules.find(
|
|
4260
|
+
(candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
|
|
4261
|
+
);
|
|
4005
4262
|
}
|
|
4006
4263
|
function stableHash(value) {
|
|
4007
4264
|
const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
@@ -4200,7 +4457,7 @@ function compileRules(rules, extraKeys) {
|
|
|
4200
4457
|
const out = /* @__PURE__ */ new Map();
|
|
4201
4458
|
const set = (rule) => {
|
|
4202
4459
|
const key = toKey(rule.key);
|
|
4203
|
-
out.set(key, { ...rule, key });
|
|
4460
|
+
out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
|
|
4204
4461
|
};
|
|
4205
4462
|
for (const key of DEFAULT_REDACT_KEYS2) {
|
|
4206
4463
|
set({ key, strategy: "full" });
|
|
@@ -4279,6 +4536,7 @@ function createRedactionProfile(profile = "local") {
|
|
|
4279
4536
|
}
|
|
4280
4537
|
var Redactor2 = class {
|
|
4281
4538
|
#rules;
|
|
4539
|
+
#sensitiveKeys;
|
|
4282
4540
|
#detectors;
|
|
4283
4541
|
#profile;
|
|
4284
4542
|
#replacement;
|
|
@@ -4291,6 +4549,7 @@ var Redactor2 = class {
|
|
|
4291
4549
|
...resolved.extraKeys,
|
|
4292
4550
|
...options?.extraKeys ?? []
|
|
4293
4551
|
]);
|
|
4552
|
+
this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
|
|
4294
4553
|
this.#detectors = [
|
|
4295
4554
|
...builtInDetectorsForProfile(this.#profile),
|
|
4296
4555
|
...options?.detectors ?? []
|
|
@@ -4324,6 +4583,14 @@ var Redactor2 = class {
|
|
|
4324
4583
|
#recordFinding(state, finding) {
|
|
4325
4584
|
if (this.#collectFindings) state.findings.push(finding);
|
|
4326
4585
|
}
|
|
4586
|
+
#detect(value, key, path30) {
|
|
4587
|
+
for (const detector of this.#detectors) {
|
|
4588
|
+
for (const detection of detector.detect({ path: path30, key, value })) {
|
|
4589
|
+
if ((detection.action ?? "replace") !== "keep") return { detector, detection };
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
return void 0;
|
|
4593
|
+
}
|
|
4327
4594
|
#redactValue(value, key, path30, depth, state) {
|
|
4328
4595
|
if (depth > this.#maxDepth) {
|
|
4329
4596
|
this.#recordFinding(
|
|
@@ -4342,6 +4609,43 @@ var Redactor2 = class {
|
|
|
4342
4609
|
return applyRule(rule, value, this.#replacement);
|
|
4343
4610
|
}
|
|
4344
4611
|
}
|
|
4612
|
+
if (typeof value === "string" && looksLikeUrlForRedaction2(value)) {
|
|
4613
|
+
const url = redactUrlString2(value, {
|
|
4614
|
+
sensitiveKeys: this.#sensitiveKeys,
|
|
4615
|
+
isSensitiveValue: (candidate) => this.#detect(candidate, key, path30) !== void 0,
|
|
4616
|
+
replacement: this.#replacement,
|
|
4617
|
+
maskPathIds: this.#profile !== "local"
|
|
4618
|
+
});
|
|
4619
|
+
if (url.value !== value) {
|
|
4620
|
+
this.#recordFinding(
|
|
4621
|
+
state,
|
|
4622
|
+
makeFinding(
|
|
4623
|
+
path30,
|
|
4624
|
+
url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
|
|
4625
|
+
"replace",
|
|
4626
|
+
"value",
|
|
4627
|
+
url.credentialRedacted ? "error" : "warning"
|
|
4628
|
+
)
|
|
4629
|
+
);
|
|
4630
|
+
const residual = this.#detect(
|
|
4631
|
+
url.value.split(this.#replacement).join(""),
|
|
4632
|
+
key,
|
|
4633
|
+
path30
|
|
4634
|
+
);
|
|
4635
|
+
if (residual === void 0) return url.value;
|
|
4636
|
+
this.#recordFinding(
|
|
4637
|
+
state,
|
|
4638
|
+
makeFinding(
|
|
4639
|
+
path30,
|
|
4640
|
+
residual.detector.id,
|
|
4641
|
+
"replace",
|
|
4642
|
+
residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
|
|
4643
|
+
residual.detection.severity ?? residual.detector.severity ?? "warning"
|
|
4644
|
+
)
|
|
4645
|
+
);
|
|
4646
|
+
return this.#replacement;
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4345
4649
|
for (const detector of this.#detectors) {
|
|
4346
4650
|
const detections = detector.detect({ path: path30, key, value });
|
|
4347
4651
|
for (const detection of detections) {
|
|
@@ -12320,7 +12624,7 @@ function envCheck(name, optional = true) {
|
|
|
12320
12624
|
async function traceDirWritable(traceDir) {
|
|
12321
12625
|
const resolved = path19.resolve(traceDir);
|
|
12322
12626
|
try {
|
|
12323
|
-
await mkdir(resolved, { recursive: true });
|
|
12627
|
+
await mkdir(resolved, { recursive: true, mode: 448 });
|
|
12324
12628
|
await access(resolved, constants.W_OK);
|
|
12325
12629
|
return {
|
|
12326
12630
|
id: "trace-dir-writable",
|
|
@@ -12338,6 +12642,44 @@ async function traceDirWritable(traceDir) {
|
|
|
12338
12642
|
};
|
|
12339
12643
|
}
|
|
12340
12644
|
}
|
|
12645
|
+
async function traceDirPermissions(traceDir) {
|
|
12646
|
+
const resolved = path19.resolve(traceDir);
|
|
12647
|
+
if (process2.platform === "win32") {
|
|
12648
|
+
return {
|
|
12649
|
+
id: "trace-dir-permissions",
|
|
12650
|
+
status: "skipped",
|
|
12651
|
+
message: "POSIX confidentiality mode check skipped on Windows.",
|
|
12652
|
+
evidence: resolved
|
|
12653
|
+
};
|
|
12654
|
+
}
|
|
12655
|
+
try {
|
|
12656
|
+
const st = await stat(resolved);
|
|
12657
|
+
const mode = st.mode & 511;
|
|
12658
|
+
const octal = mode.toString(8).padStart(3, "0");
|
|
12659
|
+
if ((mode & 63) !== 0) {
|
|
12660
|
+
return {
|
|
12661
|
+
id: "trace-dir-permissions",
|
|
12662
|
+
status: "warn",
|
|
12663
|
+
message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
|
|
12664
|
+
remediation: `Restrict with chmod 700 on the directory and review existing JSONL files with chmod 600. AgentInspect does not rewrite existing modes. See ${DOCS_BASE}/GETTING-STARTED.md`,
|
|
12665
|
+
evidence: `mode=${octal}`
|
|
12666
|
+
};
|
|
12667
|
+
}
|
|
12668
|
+
return {
|
|
12669
|
+
id: "trace-dir-permissions",
|
|
12670
|
+
status: "pass",
|
|
12671
|
+
message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
|
|
12672
|
+
evidence: `mode=${octal}`
|
|
12673
|
+
};
|
|
12674
|
+
} catch (error) {
|
|
12675
|
+
return {
|
|
12676
|
+
id: "trace-dir-permissions",
|
|
12677
|
+
status: "skipped",
|
|
12678
|
+
message: `Could not inspect trace directory permissions: ${resolved}`,
|
|
12679
|
+
evidence: error instanceof Error ? error.message : String(error)
|
|
12680
|
+
};
|
|
12681
|
+
}
|
|
12682
|
+
}
|
|
12341
12683
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
12342
12684
|
let dir = path19.dirname(path19.resolve(entryPath));
|
|
12343
12685
|
const { root } = path19.parse(dir);
|
|
@@ -12469,6 +12811,7 @@ async function runDoctorChecks(options = {}) {
|
|
|
12469
12811
|
evidence: version
|
|
12470
12812
|
},
|
|
12471
12813
|
await traceDirWritable(traceDir),
|
|
12814
|
+
await traceDirPermissions(traceDir),
|
|
12472
12815
|
envCheck("AGENT_INSPECT"),
|
|
12473
12816
|
envCheck("AGENT_INSPECT_TRACE_DIR"),
|
|
12474
12817
|
versionMismatchCheck(cwd)
|
|
@@ -12996,7 +13339,7 @@ function isModuleNotFound4(e) {
|
|
|
12996
13339
|
}
|
|
12997
13340
|
async function loadIndexSqlite() {
|
|
12998
13341
|
try {
|
|
12999
|
-
return await import('./src-
|
|
13342
|
+
return await import('./src-4WYF5RFI.mjs');
|
|
13000
13343
|
} catch (e) {
|
|
13001
13344
|
if (isModuleNotFound4(e)) {
|
|
13002
13345
|
console.error(
|