agent-inspect 6.31.3 → 6.31.4
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 +8 -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 +399 -91
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +307 -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 +217 -6
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +118 -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.4";
|
|
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,99 @@ 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 UUID_SEGMENT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1766
|
+
var HEX_SEGMENT = /^[0-9a-f]{12,}$/i;
|
|
1767
|
+
var NUMERIC_SEGMENT = /^\d{5,}$/;
|
|
1768
|
+
var OPAQUE_SEGMENT = /^[A-Za-z0-9_-]{20,}$/;
|
|
1769
|
+
function looksLikeHttpUrl(value) {
|
|
1770
|
+
return HTTP_URL.test(value.trim());
|
|
1771
|
+
}
|
|
1772
|
+
function parseHttpUrl(value) {
|
|
1773
|
+
try {
|
|
1774
|
+
const url = new URL(value.trim());
|
|
1775
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
|
|
1776
|
+
} catch {
|
|
1777
|
+
return void 0;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
function isIdentifierSegment(segment) {
|
|
1781
|
+
if (UUID_SEGMENT.test(segment)) return true;
|
|
1782
|
+
if (HEX_SEGMENT.test(segment)) return true;
|
|
1783
|
+
if (NUMERIC_SEGMENT.test(segment)) return true;
|
|
1784
|
+
return OPAQUE_SEGMENT.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
1785
|
+
}
|
|
1786
|
+
function decodeParam(value) {
|
|
1787
|
+
try {
|
|
1788
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
1789
|
+
} catch {
|
|
1790
|
+
return value;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
function redactParams(raw, options, replacement) {
|
|
1794
|
+
let redacted = false;
|
|
1795
|
+
const parts = raw.split("&").map((part) => {
|
|
1796
|
+
if (part.length === 0) return part;
|
|
1797
|
+
const separator = part.indexOf("=");
|
|
1798
|
+
if (separator < 0) return part;
|
|
1799
|
+
const rawKey = part.slice(0, separator);
|
|
1800
|
+
const rawValue = part.slice(separator + 1);
|
|
1801
|
+
const key = decodeParam(rawKey);
|
|
1802
|
+
const value = decodeParam(rawValue);
|
|
1803
|
+
if (isCredentialSensitiveKey(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
1804
|
+
redacted = true;
|
|
1805
|
+
return `${rawKey}=${replacement}`;
|
|
1806
|
+
}
|
|
1807
|
+
return part;
|
|
1808
|
+
});
|
|
1809
|
+
return { value: parts.join("&"), redacted };
|
|
1810
|
+
}
|
|
1811
|
+
function redactUrlString(value, options) {
|
|
1812
|
+
const unchanged = {
|
|
1813
|
+
value,
|
|
1814
|
+
credentialRedacted: false,
|
|
1815
|
+
identifiersMasked: false
|
|
1816
|
+
};
|
|
1817
|
+
if (!looksLikeHttpUrl(value)) return unchanged;
|
|
1818
|
+
const url = parseHttpUrl(value);
|
|
1819
|
+
if (!url) return unchanged;
|
|
1820
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
1821
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
1822
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
1823
|
+
let identifiersMasked = false;
|
|
1824
|
+
let pathname = url.pathname;
|
|
1825
|
+
if (options.maskPathIds === true) {
|
|
1826
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
1827
|
+
if (segment.length === 0 || !isIdentifierSegment(decodeParam(segment))) {
|
|
1828
|
+
return segment;
|
|
1829
|
+
}
|
|
1830
|
+
identifiersMasked = true;
|
|
1831
|
+
return idPlaceholder;
|
|
1832
|
+
}).join("/");
|
|
1833
|
+
}
|
|
1834
|
+
let search = "";
|
|
1835
|
+
if (url.search.length > 1) {
|
|
1836
|
+
const params = redactParams(url.search.slice(1), options, replacement);
|
|
1837
|
+
if (params.redacted) credentialRedacted = true;
|
|
1838
|
+
search = `?${params.value}`;
|
|
1839
|
+
} else if (url.search.length === 1) {
|
|
1840
|
+
search = "?";
|
|
1841
|
+
}
|
|
1842
|
+
let hash = url.hash;
|
|
1843
|
+
if (url.hash.includes("=")) {
|
|
1844
|
+
const params = redactParams(url.hash.slice(1), options, replacement);
|
|
1845
|
+
if (params.redacted) credentialRedacted = true;
|
|
1846
|
+
hash = `#${params.value}`;
|
|
1847
|
+
}
|
|
1848
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
1849
|
+
return {
|
|
1850
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
1851
|
+
credentialRedacted,
|
|
1852
|
+
identifiersMasked
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1760
1856
|
// packages/core/src/exporters/redact-export.ts
|
|
1761
1857
|
function isRecord4(value) {
|
|
1762
1858
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -1774,21 +1870,34 @@ function deepClone(value) {
|
|
|
1774
1870
|
}
|
|
1775
1871
|
return out;
|
|
1776
1872
|
}
|
|
1777
|
-
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1873
|
+
function boundAttributeValues(record, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1778
1874
|
if (depth > 32) {
|
|
1779
1875
|
return { truncated: true, reason: "maxDepth" };
|
|
1780
1876
|
}
|
|
1781
1877
|
const out = {};
|
|
1782
1878
|
for (const [key, value] of Object.entries(record)) {
|
|
1783
|
-
out[key] = boundValue(
|
|
1879
|
+
out[key] = boundValue(
|
|
1880
|
+
value,
|
|
1881
|
+
key,
|
|
1882
|
+
sensitiveKeys,
|
|
1883
|
+
maxMetadataValueLength,
|
|
1884
|
+
maxPreviewLength,
|
|
1885
|
+
seen,
|
|
1886
|
+
depth
|
|
1887
|
+
);
|
|
1784
1888
|
}
|
|
1785
1889
|
return out;
|
|
1786
1890
|
}
|
|
1787
|
-
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1891
|
+
function boundValue(value, key, sensitiveKeys, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
1788
1892
|
if (value === null || typeof value !== "object") {
|
|
1789
1893
|
if (typeof value === "string") {
|
|
1894
|
+
const rewritten = redactUrlString(value, {
|
|
1895
|
+
sensitiveKeys,
|
|
1896
|
+
isSensitiveValue: stringContainsHighConfidenceCredential,
|
|
1897
|
+
maskPathIds: true
|
|
1898
|
+
}).value;
|
|
1790
1899
|
return truncateStringForProfile(
|
|
1791
|
-
|
|
1900
|
+
stringContainsHighConfidenceCredential(rewritten) ? "[REDACTED]" : rewritten,
|
|
1792
1901
|
key,
|
|
1793
1902
|
maxMetadataValueLength,
|
|
1794
1903
|
maxPreviewLength
|
|
@@ -1803,6 +1912,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
1803
1912
|
(item, index) => boundValue(
|
|
1804
1913
|
item,
|
|
1805
1914
|
String(index),
|
|
1915
|
+
sensitiveKeys,
|
|
1806
1916
|
maxMetadataValueLength,
|
|
1807
1917
|
maxPreviewLength,
|
|
1808
1918
|
seen,
|
|
@@ -1812,6 +1922,7 @@ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen,
|
|
|
1812
1922
|
}
|
|
1813
1923
|
return boundAttributeValues(
|
|
1814
1924
|
value,
|
|
1925
|
+
sensitiveKeys,
|
|
1815
1926
|
maxMetadataValueLength,
|
|
1816
1927
|
maxPreviewLength,
|
|
1817
1928
|
seen,
|
|
@@ -1826,6 +1937,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
1826
1937
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
1827
1938
|
const bounded = boundAttributeValues(
|
|
1828
1939
|
redacted,
|
|
1940
|
+
[...DEFAULT_REDACT_KEYS, ...redactor.sensitiveKeys],
|
|
1829
1941
|
maxMetadataValueLength,
|
|
1830
1942
|
maxPreviewLength,
|
|
1831
1943
|
seen,
|
|
@@ -3402,7 +3514,7 @@ async function newestTraceMtimeMs(traceDir) {
|
|
|
3402
3514
|
}
|
|
3403
3515
|
async function loadSessionRuns(traceDir) {
|
|
3404
3516
|
try {
|
|
3405
|
-
const mod = await import('./src-
|
|
3517
|
+
const mod = await import('./src-4WYF5RFI.mjs');
|
|
3406
3518
|
const dbPath = mod.resolveIndexDbPath(traceDir);
|
|
3407
3519
|
const status = mod.indexStatus(dbPath);
|
|
3408
3520
|
if (!status.healthy) {
|
|
@@ -3904,7 +4016,7 @@ function isTokenCredentialKey(normalized) {
|
|
|
3904
4016
|
if (normalized.endsWith("tokens")) return false;
|
|
3905
4017
|
return normalized.endsWith("token");
|
|
3906
4018
|
}
|
|
3907
|
-
function
|
|
4019
|
+
function isCredentialSensitiveKey2(key, sensitiveKeys) {
|
|
3908
4020
|
if (!key) return false;
|
|
3909
4021
|
const normalized = normalizeSensitiveKey(key);
|
|
3910
4022
|
if (!normalized) return false;
|
|
@@ -3925,7 +4037,7 @@ function isCredentialSensitiveKey(key, sensitiveKeys) {
|
|
|
3925
4037
|
}
|
|
3926
4038
|
|
|
3927
4039
|
// 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,})`;
|
|
4040
|
+
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
4041
|
var KEY_VALUE_SECRET_PATTERN = new RegExp(
|
|
3930
4042
|
KEY_VALUE_SECRET_PATTERN_SOURCE,
|
|
3931
4043
|
"i"
|
|
@@ -3935,6 +4047,99 @@ function valueContainsKeyValueSecret(value) {
|
|
|
3935
4047
|
return KEY_VALUE_SECRET_PATTERN.test(value);
|
|
3936
4048
|
}
|
|
3937
4049
|
|
|
4050
|
+
// packages/redact/src/url-redaction.ts
|
|
4051
|
+
var HTTP_URL2 = /^https?:\/\/[^\s]+$/i;
|
|
4052
|
+
var UUID_SEGMENT2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4053
|
+
var HEX_SEGMENT2 = /^[0-9a-f]{12,}$/i;
|
|
4054
|
+
var NUMERIC_SEGMENT2 = /^\d{5,}$/;
|
|
4055
|
+
var OPAQUE_SEGMENT2 = /^[A-Za-z0-9_-]{20,}$/;
|
|
4056
|
+
function looksLikeHttpUrl2(value) {
|
|
4057
|
+
return HTTP_URL2.test(value.trim());
|
|
4058
|
+
}
|
|
4059
|
+
function parseHttpUrl2(value) {
|
|
4060
|
+
try {
|
|
4061
|
+
const url = new URL(value.trim());
|
|
4062
|
+
return url.protocol === "http:" || url.protocol === "https:" ? url : void 0;
|
|
4063
|
+
} catch {
|
|
4064
|
+
return void 0;
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
function isIdentifierSegment2(segment) {
|
|
4068
|
+
if (UUID_SEGMENT2.test(segment)) return true;
|
|
4069
|
+
if (HEX_SEGMENT2.test(segment)) return true;
|
|
4070
|
+
if (NUMERIC_SEGMENT2.test(segment)) return true;
|
|
4071
|
+
return OPAQUE_SEGMENT2.test(segment) && /\d/.test(segment) && /[A-Za-z]/.test(segment);
|
|
4072
|
+
}
|
|
4073
|
+
function decodeParam2(value) {
|
|
4074
|
+
try {
|
|
4075
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
4076
|
+
} catch {
|
|
4077
|
+
return value;
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
function redactParams2(raw, options, replacement) {
|
|
4081
|
+
let redacted = false;
|
|
4082
|
+
const parts = raw.split("&").map((part) => {
|
|
4083
|
+
if (part.length === 0) return part;
|
|
4084
|
+
const separator = part.indexOf("=");
|
|
4085
|
+
if (separator < 0) return part;
|
|
4086
|
+
const rawKey = part.slice(0, separator);
|
|
4087
|
+
const rawValue = part.slice(separator + 1);
|
|
4088
|
+
const key = decodeParam2(rawKey);
|
|
4089
|
+
const value = decodeParam2(rawValue);
|
|
4090
|
+
if (isCredentialSensitiveKey2(key, options.sensitiveKeys) || options.isSensitiveValue(value)) {
|
|
4091
|
+
redacted = true;
|
|
4092
|
+
return `${rawKey}=${replacement}`;
|
|
4093
|
+
}
|
|
4094
|
+
return part;
|
|
4095
|
+
});
|
|
4096
|
+
return { value: parts.join("&"), redacted };
|
|
4097
|
+
}
|
|
4098
|
+
function redactUrlString2(value, options) {
|
|
4099
|
+
const unchanged = {
|
|
4100
|
+
value,
|
|
4101
|
+
credentialRedacted: false,
|
|
4102
|
+
identifiersMasked: false
|
|
4103
|
+
};
|
|
4104
|
+
if (!looksLikeHttpUrl2(value)) return unchanged;
|
|
4105
|
+
const url = parseHttpUrl2(value);
|
|
4106
|
+
if (!url) return unchanged;
|
|
4107
|
+
const replacement = options.replacement ?? "[REDACTED]";
|
|
4108
|
+
const idPlaceholder = options.idPlaceholder ?? "[id]";
|
|
4109
|
+
let credentialRedacted = url.username.length > 0 || url.password.length > 0;
|
|
4110
|
+
let identifiersMasked = false;
|
|
4111
|
+
let pathname = url.pathname;
|
|
4112
|
+
if (options.maskPathIds === true) {
|
|
4113
|
+
pathname = url.pathname.split("/").map((segment) => {
|
|
4114
|
+
if (segment.length === 0 || !isIdentifierSegment2(decodeParam2(segment))) {
|
|
4115
|
+
return segment;
|
|
4116
|
+
}
|
|
4117
|
+
identifiersMasked = true;
|
|
4118
|
+
return idPlaceholder;
|
|
4119
|
+
}).join("/");
|
|
4120
|
+
}
|
|
4121
|
+
let search = "";
|
|
4122
|
+
if (url.search.length > 1) {
|
|
4123
|
+
const params = redactParams2(url.search.slice(1), options, replacement);
|
|
4124
|
+
if (params.redacted) credentialRedacted = true;
|
|
4125
|
+
search = `?${params.value}`;
|
|
4126
|
+
} else if (url.search.length === 1) {
|
|
4127
|
+
search = "?";
|
|
4128
|
+
}
|
|
4129
|
+
let hash = url.hash;
|
|
4130
|
+
if (url.hash.includes("=")) {
|
|
4131
|
+
const params = redactParams2(url.hash.slice(1), options, replacement);
|
|
4132
|
+
if (params.redacted) credentialRedacted = true;
|
|
4133
|
+
hash = `#${params.value}`;
|
|
4134
|
+
}
|
|
4135
|
+
if (!credentialRedacted && !identifiersMasked) return unchanged;
|
|
4136
|
+
return {
|
|
4137
|
+
value: `${url.protocol}//${url.host}${pathname}${search}${hash}`,
|
|
4138
|
+
credentialRedacted,
|
|
4139
|
+
identifiersMasked
|
|
4140
|
+
};
|
|
4141
|
+
}
|
|
4142
|
+
|
|
3938
4143
|
// packages/redact/src/index.ts
|
|
3939
4144
|
var DEFAULT_REDACT_KEYS2 = [
|
|
3940
4145
|
"authorization",
|
|
@@ -4001,7 +4206,9 @@ function findCompiledKeyRule(key, rules) {
|
|
|
4001
4206
|
const exact = toKey(key);
|
|
4002
4207
|
const direct = rules.find((candidate) => candidate.key === exact);
|
|
4003
4208
|
if (direct) return direct;
|
|
4004
|
-
return rules.find(
|
|
4209
|
+
return rules.find(
|
|
4210
|
+
(candidate) => isCredentialSensitiveKey2(key, [candidate.raw ?? candidate.key])
|
|
4211
|
+
);
|
|
4005
4212
|
}
|
|
4006
4213
|
function stableHash(value) {
|
|
4007
4214
|
const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
@@ -4200,7 +4407,7 @@ function compileRules(rules, extraKeys) {
|
|
|
4200
4407
|
const out = /* @__PURE__ */ new Map();
|
|
4201
4408
|
const set = (rule) => {
|
|
4202
4409
|
const key = toKey(rule.key);
|
|
4203
|
-
out.set(key, { ...rule, key });
|
|
4410
|
+
out.set(key, { ...rule, key, raw: rule.raw ?? rule.key });
|
|
4204
4411
|
};
|
|
4205
4412
|
for (const key of DEFAULT_REDACT_KEYS2) {
|
|
4206
4413
|
set({ key, strategy: "full" });
|
|
@@ -4279,6 +4486,7 @@ function createRedactionProfile(profile = "local") {
|
|
|
4279
4486
|
}
|
|
4280
4487
|
var Redactor2 = class {
|
|
4281
4488
|
#rules;
|
|
4489
|
+
#sensitiveKeys;
|
|
4282
4490
|
#detectors;
|
|
4283
4491
|
#profile;
|
|
4284
4492
|
#replacement;
|
|
@@ -4291,6 +4499,7 @@ var Redactor2 = class {
|
|
|
4291
4499
|
...resolved.extraKeys,
|
|
4292
4500
|
...options?.extraKeys ?? []
|
|
4293
4501
|
]);
|
|
4502
|
+
this.#sensitiveKeys = this.#rules.map((rule) => rule.raw ?? rule.key);
|
|
4294
4503
|
this.#detectors = [
|
|
4295
4504
|
...builtInDetectorsForProfile(this.#profile),
|
|
4296
4505
|
...options?.detectors ?? []
|
|
@@ -4324,6 +4533,14 @@ var Redactor2 = class {
|
|
|
4324
4533
|
#recordFinding(state, finding) {
|
|
4325
4534
|
if (this.#collectFindings) state.findings.push(finding);
|
|
4326
4535
|
}
|
|
4536
|
+
#detect(value, key, path30) {
|
|
4537
|
+
for (const detector of this.#detectors) {
|
|
4538
|
+
for (const detection of detector.detect({ path: path30, key, value })) {
|
|
4539
|
+
if ((detection.action ?? "replace") !== "keep") return { detector, detection };
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
return void 0;
|
|
4543
|
+
}
|
|
4327
4544
|
#redactValue(value, key, path30, depth, state) {
|
|
4328
4545
|
if (depth > this.#maxDepth) {
|
|
4329
4546
|
this.#recordFinding(
|
|
@@ -4342,6 +4559,43 @@ var Redactor2 = class {
|
|
|
4342
4559
|
return applyRule(rule, value, this.#replacement);
|
|
4343
4560
|
}
|
|
4344
4561
|
}
|
|
4562
|
+
if (typeof value === "string" && looksLikeHttpUrl2(value)) {
|
|
4563
|
+
const url = redactUrlString2(value, {
|
|
4564
|
+
sensitiveKeys: this.#sensitiveKeys,
|
|
4565
|
+
isSensitiveValue: (candidate) => this.#detect(candidate, key, path30) !== void 0,
|
|
4566
|
+
replacement: this.#replacement,
|
|
4567
|
+
maskPathIds: this.#profile !== "local"
|
|
4568
|
+
});
|
|
4569
|
+
if (url.value !== value) {
|
|
4570
|
+
this.#recordFinding(
|
|
4571
|
+
state,
|
|
4572
|
+
makeFinding(
|
|
4573
|
+
path30,
|
|
4574
|
+
url.credentialRedacted ? "value.urlCredential" : "value.urlIdentifier",
|
|
4575
|
+
"replace",
|
|
4576
|
+
"value",
|
|
4577
|
+
url.credentialRedacted ? "error" : "warning"
|
|
4578
|
+
)
|
|
4579
|
+
);
|
|
4580
|
+
const residual = this.#detect(
|
|
4581
|
+
url.value.split(this.#replacement).join(""),
|
|
4582
|
+
key,
|
|
4583
|
+
path30
|
|
4584
|
+
);
|
|
4585
|
+
if (residual === void 0) return url.value;
|
|
4586
|
+
this.#recordFinding(
|
|
4587
|
+
state,
|
|
4588
|
+
makeFinding(
|
|
4589
|
+
path30,
|
|
4590
|
+
residual.detector.id,
|
|
4591
|
+
"replace",
|
|
4592
|
+
residual.detection.matchKind ?? residual.detector.matchKind ?? "custom",
|
|
4593
|
+
residual.detection.severity ?? residual.detector.severity ?? "warning"
|
|
4594
|
+
)
|
|
4595
|
+
);
|
|
4596
|
+
return this.#replacement;
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4345
4599
|
for (const detector of this.#detectors) {
|
|
4346
4600
|
const detections = detector.detect({ path: path30, key, value });
|
|
4347
4601
|
for (const detection of detections) {
|
|
@@ -12320,7 +12574,7 @@ function envCheck(name, optional = true) {
|
|
|
12320
12574
|
async function traceDirWritable(traceDir) {
|
|
12321
12575
|
const resolved = path19.resolve(traceDir);
|
|
12322
12576
|
try {
|
|
12323
|
-
await mkdir(resolved, { recursive: true });
|
|
12577
|
+
await mkdir(resolved, { recursive: true, mode: 448 });
|
|
12324
12578
|
await access(resolved, constants.W_OK);
|
|
12325
12579
|
return {
|
|
12326
12580
|
id: "trace-dir-writable",
|
|
@@ -12338,6 +12592,44 @@ async function traceDirWritable(traceDir) {
|
|
|
12338
12592
|
};
|
|
12339
12593
|
}
|
|
12340
12594
|
}
|
|
12595
|
+
async function traceDirPermissions(traceDir) {
|
|
12596
|
+
const resolved = path19.resolve(traceDir);
|
|
12597
|
+
if (process2.platform === "win32") {
|
|
12598
|
+
return {
|
|
12599
|
+
id: "trace-dir-permissions",
|
|
12600
|
+
status: "skipped",
|
|
12601
|
+
message: "POSIX confidentiality mode check skipped on Windows.",
|
|
12602
|
+
evidence: resolved
|
|
12603
|
+
};
|
|
12604
|
+
}
|
|
12605
|
+
try {
|
|
12606
|
+
const st = await stat(resolved);
|
|
12607
|
+
const mode = st.mode & 511;
|
|
12608
|
+
const octal = mode.toString(8).padStart(3, "0");
|
|
12609
|
+
if ((mode & 63) !== 0) {
|
|
12610
|
+
return {
|
|
12611
|
+
id: "trace-dir-permissions",
|
|
12612
|
+
status: "warn",
|
|
12613
|
+
message: `Trace directory allows group/other access (mode ${octal}): ${resolved}`,
|
|
12614
|
+
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`,
|
|
12615
|
+
evidence: `mode=${octal}`
|
|
12616
|
+
};
|
|
12617
|
+
}
|
|
12618
|
+
return {
|
|
12619
|
+
id: "trace-dir-permissions",
|
|
12620
|
+
status: "pass",
|
|
12621
|
+
message: `Trace directory mode is restrictive (${octal}): ${resolved}`,
|
|
12622
|
+
evidence: `mode=${octal}`
|
|
12623
|
+
};
|
|
12624
|
+
} catch (error) {
|
|
12625
|
+
return {
|
|
12626
|
+
id: "trace-dir-permissions",
|
|
12627
|
+
status: "skipped",
|
|
12628
|
+
message: `Could not inspect trace directory permissions: ${resolved}`,
|
|
12629
|
+
evidence: error instanceof Error ? error.message : String(error)
|
|
12630
|
+
};
|
|
12631
|
+
}
|
|
12632
|
+
}
|
|
12341
12633
|
function readPackageVersionNearEntry(entryPath, packageName) {
|
|
12342
12634
|
let dir = path19.dirname(path19.resolve(entryPath));
|
|
12343
12635
|
const { root } = path19.parse(dir);
|
|
@@ -12469,6 +12761,7 @@ async function runDoctorChecks(options = {}) {
|
|
|
12469
12761
|
evidence: version
|
|
12470
12762
|
},
|
|
12471
12763
|
await traceDirWritable(traceDir),
|
|
12764
|
+
await traceDirPermissions(traceDir),
|
|
12472
12765
|
envCheck("AGENT_INSPECT"),
|
|
12473
12766
|
envCheck("AGENT_INSPECT_TRACE_DIR"),
|
|
12474
12767
|
versionMismatchCheck(cwd)
|
|
@@ -12996,7 +13289,7 @@ function isModuleNotFound4(e) {
|
|
|
12996
13289
|
}
|
|
12997
13290
|
async function loadIndexSqlite() {
|
|
12998
13291
|
try {
|
|
12999
|
-
return await import('./src-
|
|
13292
|
+
return await import('./src-4WYF5RFI.mjs');
|
|
13000
13293
|
} catch (e) {
|
|
13001
13294
|
if (isModuleNotFound4(e)) {
|
|
13002
13295
|
console.error(
|