Redact existing placeholder keys without secrets

This commit is contained in:
Mathews-Tom
2026-06-17 22:22:34 +05:30
parent de8d81e278
commit 1a6e149102
3 changed files with 34 additions and 10 deletions
+2 -2
View File
@@ -2,12 +2,12 @@
## [Unreleased]
## [16.0.5] - 2026-06-17
### Added
- Added `friendlyName` support for hidden secrets so model-visible placeholders can carry sanitized semantic labels, content-derived hashes, and case hints while preserving exact deobfuscation ([#2465](https://github.com/can1357/oh-my-pi/issues/2465)).
## [16.0.5] - 2026-06-17
### Added
- Added `tui.tight` setting (default `false`) to enable tight layout by removing the 1-character horizontal padding from terminal output.
+9 -4
View File
@@ -1222,17 +1222,22 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
const fileEntries = await logger.time("loadSecrets", loadSecrets, cwd, agentDir);
const envEntries = collectEnvSecrets();
const allEntries = [...envEntries, ...fileEntries];
const needsPlaceholderKey = allEntries.some(entry => (entry.mode ?? "obfuscate") === "obfuscate");
const placeholderKey = needsPlaceholderKey
? await getSecretPlaceholderKey()
: await getExistingSecretPlaceholderKey();
if (allEntries.length > 0) {
// The persisted placeholder key — and creating its key file under the
// config root — is only needed for reversible obfuscate-mode placeholders.
// Replace-mode entries never build a keyed base, so a replace-only secrets
// set must not require the key; otherwise a headless run with an unwritable
// config root fails startup for a feature it does not use.
const needsPlaceholderKey = allEntries.some(entry => (entry.mode ?? "obfuscate") === "obfuscate");
const placeholderKey = needsPlaceholderKey
? await getSecretPlaceholderKey()
: await getExistingSecretPlaceholderKey();
obfuscator = new SecretObfuscator(allEntries, placeholderKey);
} else if (placeholderKey !== undefined) {
obfuscator = new SecretObfuscator(
[{ type: "plain", mode: "replace", content: placeholderKey }],
placeholderKey,
);
}
}
const secretsEnabled = obfuscator?.hasSecrets() === true;
@@ -170,11 +170,18 @@ describe("createAgentSession session storage isolation", () => {
enableLsp: false,
};
const withoutSecrets = await createAgentSession(commonOptions);
const existingKeySpy = spyOn(secrets, "getExistingSecretPlaceholderKey").mockImplementation(
async () => undefined,
);
try {
expect(withoutSecrets.session.systemPrompt.join("\n")).not.toContain(redactionGuidance);
const withoutSecrets = await createAgentSession(commonOptions);
try {
expect(withoutSecrets.session.systemPrompt.join("\n")).not.toContain(redactionGuidance);
} finally {
await withoutSecrets.session.dispose();
}
} finally {
await withoutSecrets.session.dispose();
existingKeySpy.mockRestore();
}
fs.mkdirSync(path.join(cwd, ".omp"), { recursive: true });
@@ -258,7 +265,7 @@ describe("createAgentSession session storage isolation", () => {
});
});
it("requests the placeholder key only when an obfuscate-mode secret is configured", async () => {
it("creates the placeholder key only when an obfuscate-mode secret is configured", async () => {
await withClearedSecretEnv(async () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `pi-sdk-secrets-key-${Snowflake.next()}-`));
tempDirs.push(tempDir);
@@ -287,6 +294,18 @@ describe("createAgentSession session storage isolation", () => {
async () => "existing-placeholder-key",
);
try {
const keyOnly = await createAgentSession(commonOptions);
try {
expect(keySpy).not.toHaveBeenCalled();
expect(existingKeySpy).toHaveBeenCalled();
expect(keyOnly.session.obfuscator?.obfuscate("existing-placeholder-key")).not.toContain(
"existing-placeholder-key",
);
} finally {
await keyOnly.session.dispose();
}
existingKeySpy.mockClear();
// Replace-mode secrets never build a reversible keyed placeholder, so
// startup must not create the key file; an existing key is still redacted.
fs.writeFileSync(