fix(terminal): reject a negative provenance config base

A marker whose `configBase` is negative passed `Number.isSafeInteger`, so
`restoreIndexedConfig` treated index 0 as a guard slot and deleted a
`credential.interactive` pair the user had configured themselves. The
adjacent "not a safe integer" case was already rejected; -1 slipped
through because it is one.
This commit is contained in:
Brennan Benson
2026-08-28 06:38:34 -07:00
parent 9a7f69d81c
commit e6054e671d
2 changed files with 17 additions and 1 deletions
@@ -86,6 +86,18 @@ describe('restoreUnguardedGitCredentialEnv marker handling', () => {
}
})
// A negative base is not a position the guard can ever have appended at, but it
// still satisfies Number.isSafeInteger. Left unvalidated it makes index 0 look
// guard-owned and deletes a pair the user configured themselves.
it('ignores a negative configBase instead of treating index 0 as a guard slot', () => {
const env = guardedEnv('{"v":1,"env":{},"configBase":-1}')
restoreUnguardedGitCredentialEnv(env)
expect(env.GIT_CONFIG_COUNT).toBe('2')
expect(env.GIT_CONFIG_KEY_0).toBe('credential.interactive')
expect(env.GIT_CONFIG_VALUE_0).toBe('false')
expect(env.GIT_CONFIG_KEY_1).toBe('credential.guiPrompt')
})
// A marker that names no config base did not append config, so nothing indexed
// in this environment is Orca's — including a pair that looks exactly like ours.
it('never removes indexed config when the marker claims no config base', () => {
@@ -90,8 +90,12 @@ function readRestoreState(raw: string): RestoreState | null {
return {
v: 1,
env: Object.fromEntries(entries),
// Why: a negative base is a position the guard cannot have appended at, and
// would make slot 0 look guard-owned and delete a pair the user set.
configBase:
typeof configBase === 'number' && Number.isSafeInteger(configBase) ? configBase : null
typeof configBase === 'number' && Number.isSafeInteger(configBase) && configBase >= 0
? configBase
: null
}
}