Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions packages/acp-bridge/src/bridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4692,14 +4692,18 @@ describe('createAcpSessionBridge', () => {
);
});

it('rejects NaN maxSessions (BRApy: silent fail-OPEN guard)', () => {
it.each([
['NaN', Number.NaN],
['negative', -5],
['float', 1.5],
])('rejects invalid maxSessions (%s)', (_label, value) => {
// A typo / parse error in CLI / config that yields NaN must
// NOT silently disable the daemon's resource cap. We fail
// boot loud instead of serving unbounded.
expect(() => makeBridge({ maxSessions: NaN })).toThrow(
/maxSessions: NaN/,
);
expect(() => makeBridge({ maxSessions: -5 })).toThrow(/maxSessions: -5/);
expect(() => makeBridge({ maxSessions: value })).toThrow(/maxSessions/);
});

it('accepts disabled maxSessions sentinels', () => {
// Explicit zero or Infinity remain valid "unlimited" sentinels.
expect(() => makeBridge({ maxSessions: 0 })).not.toThrow();
expect(() => makeBridge({ maxSessions: Infinity })).not.toThrow();
Expand Down
26 changes: 13 additions & 13 deletions packages/acp-bridge/src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -710,25 +710,25 @@ export function createAcpSessionBridge(opts: BridgeOptions): AcpSessionBridge {
// `0` → explicitly unlimited (operator opt-out).
// `Infinity` → unlimited (programmatic opt-out — accepted as a
// long-standing alias since the cap check is `>= max`).
// `NaN` / negative → throw. A typo / parse error in CLI/config
// silently disabling the daemon's only resource
// guard is fail-OPEN behavior — we'd rather fail
// boot than serve unbounded.
// non-integer / `NaN` / negative → throw. A typo / parse error in
// CLI/config silently disabling or rounding the
// daemon's only resource guard is fail-OPEN behavior —
// we'd rather fail boot than serve with the wrong cap.
let maxSessions: number;
if (opts.maxSessions === undefined) {
maxSessions = DEFAULT_MAX_SESSIONS;
} else if (Number.isNaN(opts.maxSessions)) {
throw new TypeError(
`Invalid maxSessions: NaN. Must be a number >= 0 ` +
`(0 / Infinity = unlimited).`,
);
} else if (opts.maxSessions < 0) {
} else if (opts.maxSessions === 0 || opts.maxSessions === Infinity) {
maxSessions = Infinity;
} else if (
!Number.isFinite(opts.maxSessions) ||
!Number.isInteger(opts.maxSessions) ||
opts.maxSessions < 0
) {
throw new TypeError(
`Invalid maxSessions: ${opts.maxSessions}. Must be >= 0 ` +
`Invalid maxSessions: ${opts.maxSessions}. ` +
`Must be a non-negative integer ` +
`(0 / Infinity = unlimited).`,
);
} else if (opts.maxSessions === 0 || opts.maxSessions === Infinity) {
maxSessions = Infinity;
} else {
maxSessions = opts.maxSessions;
}
Expand Down
Loading