Skip to content

Commit 935ac15

Browse files
vlydevVladyslav Lyshenko
authored andcommitted
fix: handle hex payloads whose first byte starts with 'A'
Previously, extractHex() would match the INSPECT_URL_RE pattern (?:%20|\s|\+)A([0-9A-Fa-f]+) and strip the leading 'A', treating it as the classic asset-ID prefix marker. When the actual XOR key byte happened to encode as hex 'A' (i.e. 0xAx), this produced an odd-length hex string (e.g. 47 chars) which caused hex2bin() to return false and throw InvalidArgumentException. Fix: add an even-length guard — if stripping 'A' yields an odd number of hex characters, 'A' is part of the payload, not a prefix marker. Fall through to the pure-masked regex which captures the full hex blob (including the leading 'A'). Add regression tests for URL and bare-hex forms of a payload whose XOR key byte is 0xA6, verifying defindex = 1377 after decryption.
1 parent a1e4d97 commit 935ac15

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

src/InspectLink.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,16 @@ private static function extractHex(string $input): string
109109
return $m[1];
110110
}
111111

112-
// Classic/market URL: A<hex> preceded by %20, space, or + (A is a prefix marker, not hex)
113-
if (preg_match('/(?:%20|\s|\+)A([0-9A-Fa-f]+)/i', $stripped, $m)) {
112+
// Classic/market URL: A<hex> preceded by %20, space, or + (A is a prefix marker, not hex).
113+
// If stripping A yields odd-length hex, A is actually the first byte of the payload —
114+
// fall through to the pure-masked check below which captures it with A included.
115+
if (preg_match('/(?:%20|\s|\+)A([0-9A-Fa-f]+)/i', $stripped, $m)
116+
&& 0 === strlen($m[1]) % 2) {
114117
return $m[1];
115118
}
116119

117-
// Pure masked format: csgo_econ_action_preview%20<hexblob> (no S/A/M prefix)
120+
// Pure masked format: csgo_econ_action_preview%20<hexblob> (no S/A/M prefix).
121+
// Also handles payloads whose first hex character happens to be A.
118122
if (preg_match('/csgo_econ_action_preview(?:%20|\s|\+)([0-9A-Fa-f]{10,})$/i', $stripped, $m)) {
119123
return $m[1];
120124
}

tests/InspectLinkTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,45 @@ public function testDeserializeHybridUrlReturnsCorrectItemid(): void
339339
$this->assertSame(50075495125, $item->itemid);
340340
}
341341

342+
// -----------------------------------------------------------------------
343+
// Regression: hex payload starting with 'A' (key byte = 0xAx)
344+
// -----------------------------------------------------------------------
345+
346+
/**
347+
* A masked link whose proto payload begins with hex 'A6' (XOR key = 0xA6).
348+
* Previously, extractHex() wrongly treated the 'A' as the classic asset-ID
349+
* prefix marker and stripped it, producing 47 hex chars (odd length) which
350+
* caused hex2bin to return false / an InvalidArgumentException.
351+
*/
352+
private const A_START_HEX = 'A6B617190F659DBE47AC86A68EA096A2CEB4D6AFBFFA9FD2';
353+
354+
private const A_START_URL = 'steam://run/730//+csgo_econ_action_preview%20' . self::A_START_HEX;
355+
356+
public function testIsMaskedReturnsTrueForAStartingPayload(): void
357+
{
358+
$this->assertTrue(InspectLink::isMasked(self::A_START_URL));
359+
}
360+
361+
public function testDeserializeAStartingPayloadDoesNotThrow(): void
362+
{
363+
$item = InspectLink::deserialize(self::A_START_URL);
364+
// Payload has XOR key 0xA6; after decryption defindex decodes to 1377.
365+
$this->assertSame(1377, $item->defindex);
366+
}
367+
368+
public function testDeserializeAStartingBareHexDoesNotThrow(): void
369+
{
370+
$item = InspectLink::deserialize(self::A_START_HEX);
371+
$this->assertSame(1377, $item->defindex);
372+
}
373+
374+
public function testAStartingPayloadInsideSteamRunguameUrl(): void
375+
{
376+
$url = 'steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20' . self::A_START_HEX;
377+
$item = InspectLink::deserialize($url);
378+
$this->assertSame(1377, $item->defindex);
379+
}
380+
342381
// -----------------------------------------------------------------------
343382
// Checksum correctness
344383
// -----------------------------------------------------------------------

0 commit comments

Comments
 (0)