Skip to content

Commit d022b21

Browse files
anandgupta42claude
andauthored
fix: prototype pollution and shallow freeze in SkillFollowups.get() (#558)
Adversarial testing for v0.5.14 release found 2 bugs: 1. **Prototype pollution:** `FOLLOWUPS["__proto__"]` traversed `Object.prototype` instead of returning `[]`. Fixed with `Object.hasOwn()` guard. 2. **Shallow freeze:** `Object.freeze()` on the array didn't freeze nested suggestion objects, allowing shared state mutation across callers. Fixed with deep copy via `Object.freeze({ ...s })`. Includes 52 adversarial tests covering injection attacks, boundary values, immutability, concurrency, and data integrity for `SkillFollowups`, `Locale.duration`, and `Dispatcher.reset`. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dbff413 commit d022b21

File tree

2 files changed

+417
-1
lines changed

2 files changed

+417
-1
lines changed

packages/opencode/src/skill/followups.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,13 @@ export namespace SkillFollowups {
153153
const WAREHOUSE_NUDGE =
154154
"**Tip:** Connect a warehouse to validate against real data. Run `/discover` to auto-detect your connections."
155155

156+
// altimate_change start — upstream_fix: guard against prototype pollution and deep-freeze suggestions
156157
export function get(skillName: string): readonly Suggestion[] {
157-
return Object.freeze(FOLLOWUPS[skillName] ?? [])
158+
if (!Object.hasOwn(FOLLOWUPS, skillName)) return Object.freeze([])
159+
const suggestions = FOLLOWUPS[skillName]!
160+
return Object.freeze(suggestions.map((s) => Object.freeze({ ...s })))
158161
}
162+
// altimate_change end
159163

160164
export function format(skillName: string): string {
161165
const suggestions = get(skillName)

0 commit comments

Comments
 (0)