@henols/vice-mcp 0.1.11 → 0.1.12

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.
Files changed (2) hide show
  1. package/disasm-decoder.ts +28 -4
  2. package/package.json +1 -1
package/disasm-decoder.ts CHANGED
@@ -5,7 +5,10 @@
5
5
  // CPU-history decode (GAIN-01) import THIS file directly, never a tool
6
6
  // module, so a protocol import here would force those consumers to pull in
7
7
  // transport code they do not need. This module has no emulator, no
8
- // protocol, no network -- its only input is a byte array.
8
+ // protocol, no network -- its only input is a byte array. Note: DERIV-02 and
9
+ // GAIN-01 were both cut from v0.2.0 scope on 2026-08-17 -- see the
10
+ // startAddress bound below, which is now defense-in-depth on a currently
11
+ // unreachable path rather than a guard against a live in-process caller.
9
12
  //
10
13
  // ---------------------------------------------------------------------------
11
14
  // WHY THIS FILE EXISTS RATHER THAN LIVING INSIDE THE TOOL HANDLER
@@ -30,6 +33,13 @@
30
33
  // - Never add recursion or an unbounded loop -- the bound is what makes an
31
34
  // attacker-controlled memory image safe to decode (04-RESEARCH.md
32
35
  // Security Domain, T-04-03-01).
36
+ // - Never fold the `<= 0xffff` upper bound into `isNonNegativeSafeInteger()`
37
+ // or apply it to `opts.count`/`opts.end` -- those two stay unbounded by
38
+ // design (04-REVIEW.md IN-03: the loop is always bounded by
39
+ // `bytes.length`, so an absurd value degrades to "no effective limit",
40
+ // never a crash or hang). Bounding them would be a behaviour change
41
+ // dressed up as a consistency fix. The upper bound belongs only in the
42
+ // separate `isValidStartAddress()` guard below.
33
43
 
34
44
  import { OPCODES, type AddressingMode } from "./disasm-opcodes.ts";
35
45
 
@@ -94,6 +104,18 @@ function isNonNegativeSafeInteger(value: unknown): value is number {
94
104
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
95
105
  }
96
106
 
107
+ /** True iff `value` is a valid `startAddress` -- a safe, non-negative integer
108
+ * that additionally fits in the C64's 16-bit address space (`<= 0xffff`).
109
+ * Deliberately separate from `isNonNegativeSafeInteger()` rather than an
110
+ * upper bound folded into it: `opts.count`/`opts.end` must stay unbounded
111
+ * (see the `WHAT NOT TO DO` block above and 04-REVIEW.md IN-03), so only
112
+ * `startAddress` gets this stricter narrowing. Mirrors `stock-address.ts`'s
113
+ * `inAddressRange()` without importing it -- D-05 keeps this module's only
114
+ * import to `./disasm-opcodes.ts`. */
115
+ function isValidStartAddress(value: unknown): value is number {
116
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= 0xffff;
117
+ }
118
+
97
119
  /** Interprets `b` as an 8-bit two's-complement signed byte, per DISASM-04's
98
120
  * branch resolution rule: `signed8(b) = b < 0x80 ? b : b - 0x100`. */
99
121
  function signed8(b: number): number {
@@ -118,12 +140,14 @@ function buildNotes(flags: { truncated: boolean; pageWrap: boolean; illegal: boo
118
140
  * `startAddress`. Bounded by construction (T-04-03-01): a single `while`
119
141
  * loop over a byte cursor, every iteration consumes at least one byte, no
120
142
  * recursion anywhere in this file. Never throws -- malformed input (a
121
- * non-`Uint8Array` `bytes`, a negative or non-integer `startAddress`)
122
- * returns `[]`.
143
+ * non-`Uint8Array` `bytes`, a negative or non-integer `startAddress`, or a
144
+ * `startAddress` above `0xffff`, the top of the 16-bit address space)
145
+ * returns `[]` rather than wrapping the address into range and returning a
146
+ * plausible-looking but wrong listing.
123
147
  */
124
148
  export function decode(bytes: Uint8Array, startAddress: number, opts: DecodeOptions = {}): Instruction[] {
125
149
  if (!(bytes instanceof Uint8Array)) return [];
126
- if (!isNonNegativeSafeInteger(startAddress)) return [];
150
+ if (!isValidStartAddress(startAddress)) return [];
127
151
 
128
152
  const options = isPlainObject(opts) ? opts : {};
129
153
  const count = isNonNegativeSafeInteger(options.count) ? options.count : undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henols/vice-mcp",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "VICE emulator MCP server for C64 reverse-engineering: a stdio MCP server that proxies vice tools to a host VICE MCP server.",
5
5
  "type": "module",
6
6
  "bin": {