Skip to content

Commit 0faaa4c

Browse files
authored
Potential fix for bug in id3v2 frame header generation (#91)
1 parent e72eb1f commit 0faaa4c

2 files changed

Lines changed: 147 additions & 5 deletions

File tree

src/id3v2/frames/frameHeader.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ export class Id3v2FrameHeader {
9797
throw new Error("Data must contain at least a frame ID");
9898
}
9999

100-
let frameId;
100+
let rawFrameId: string;
101+
let frameId: FrameIdentifier;
101102
let flags = 0;
102103
let frameSize = 0;
103104
switch (version) {
@@ -107,7 +108,8 @@ export class Id3v2FrameHeader {
107108
}
108109

109110
// Set frame ID -- first 3 bytes
110-
frameId = FrameIdentifiers[data.subarray(0, 3).toString(StringType.Latin1)];
111+
rawFrameId = data.subarray(0, 3).toString(StringType.Latin1);
112+
frameId = FrameIdentifiers[rawFrameId] || new FrameIdentifier(undefined, undefined, rawFrameId);
111113

112114
// If the full header information was not passed in, do not continue to the steps
113115
// to parse the frame size and flags.
@@ -124,7 +126,8 @@ export class Id3v2FrameHeader {
124126
}
125127

126128
// Set the frame ID -- first 4 bytes
127-
frameId = FrameIdentifiers[data.subarray(0, 4).toString(StringType.Latin1)];
129+
rawFrameId = data.subarray(0, 4).toString(StringType.Latin1);
130+
frameId = FrameIdentifiers[rawFrameId] || new FrameIdentifier(undefined, rawFrameId, undefined);
128131

129132
// If the full header information was not passed in, do not continue to the steps
130133
// to parse the frame size and flags.
@@ -147,12 +150,13 @@ export class Id3v2FrameHeader {
147150
}
148151

149152
// Set the frame ID -- the first 4 bytes
150-
frameId = FrameIdentifiers[data.subarray(0, 4).toString(StringType.Latin1)];
153+
rawFrameId = data.subarray(0, 4).toString(StringType.Latin1);
154+
frameId = FrameIdentifiers[rawFrameId] || new FrameIdentifier(rawFrameId, undefined, undefined);
151155

152156
// If the full header information was not passed in, do not continue to the steps to
153157
// ... eh, you probably get it by now.
154158
if (data.length < 10) {
155-
return;
159+
break;
156160
}
157161

158162
frameSize = SyncData.toUint(data.subarray(4, 4));
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import {suite, test} from "@testdeck/mocha";
2+
import {assert} from "chai";
3+
4+
import {ByteVector, StringType} from "../../src/byteVector";
5+
import {Id3v2FrameHeader} from "../../src/id3v2/frames/frameHeader";
6+
import {Testers} from "../utilities/testers";
7+
import {FrameIdentifiers} from "../../src/id3v2/frameIdentifiers";
8+
9+
@suite class FrameHeaderTests {
10+
@test
11+
public fromData_invalid() {
12+
// Arrange
13+
const testBytes = ByteVector.empty();
14+
15+
// Act / Assert
16+
Testers.testTruthy((v: ByteVector) => Id3v2FrameHeader.fromData(v, 3));
17+
Testers.testUint((v) => Id3v2FrameHeader.fromData(testBytes, v));
18+
assert.throws(() => Id3v2FrameHeader.fromData(testBytes, 1));
19+
assert.throws(() => Id3v2FrameHeader.fromData(testBytes, 5));
20+
}
21+
22+
@test
23+
public fromData_v2_tooShort() {
24+
// Arrange
25+
const testBytes = ByteVector.fromSize(2);
26+
27+
// Act / Assert
28+
assert.throws(() => Id3v2FrameHeader.fromData(testBytes, 2));
29+
}
30+
31+
@test
32+
public fromData_v2_standardIdentifier() {
33+
// Arrange
34+
const testBytes = ByteVector.fromString("TT1", StringType.Latin1);
35+
36+
// Act
37+
const header = Id3v2FrameHeader.fromData(testBytes, 2);
38+
39+
// Assert
40+
assert.isOk(header);
41+
assert.strictEqual(header.frameId, FrameIdentifiers['TIT1']);
42+
assert.strictEqual(header.flags, 0);
43+
assert.strictEqual(header.frameSize, 0);
44+
}
45+
46+
@test
47+
public fromData_v2_nonstandardIdentifier() {
48+
// Arrange
49+
const testBytes = ByteVector.fromString("NON", StringType.Latin1);
50+
51+
// Act
52+
const header = Id3v2FrameHeader.fromData(testBytes, 2);
53+
54+
// Assert
55+
assert.isOk(header);
56+
Testers.bvEqual(header.frameId.render(2), testBytes);
57+
assert.strictEqual(header.flags, 0);
58+
assert.strictEqual(header.frameSize, 0);
59+
}
60+
61+
@test
62+
public fromData_v3_tooShort() {
63+
// Arrange
64+
const testBytes = ByteVector.fromSize(3);
65+
66+
// Act / Assert
67+
assert.throws(() => Id3v2FrameHeader.fromData(testBytes, 3));
68+
}
69+
70+
@test
71+
public fromData_v3_standardIdentifier() {
72+
// Arrange
73+
const testBytes = ByteVector.fromString("TIT1", StringType.Latin1);
74+
75+
// Act
76+
const header = Id3v2FrameHeader.fromData(testBytes, 3);
77+
78+
// Assert
79+
assert.isOk(header);
80+
assert.strictEqual(header.frameId, FrameIdentifiers['TIT1']);
81+
assert.strictEqual(header.flags, 0);
82+
assert.strictEqual(header.frameSize, 0);
83+
}
84+
85+
@test
86+
public fromData_v3_nonstandardIdentifier() {
87+
// Arrange
88+
const testBytes = ByteVector.fromString("NON1", StringType.Latin1);
89+
90+
// Act
91+
const header = Id3v2FrameHeader.fromData(testBytes, 3);
92+
93+
// Assert
94+
assert.isOk(header);
95+
Testers.bvEqual(header.frameId.render(3), testBytes);
96+
assert.strictEqual(header.flags, 0);
97+
assert.strictEqual(header.frameSize, 0);
98+
}
99+
100+
@test
101+
public fromData_v4_tooShort() {
102+
// Arrange
103+
const testBytes = ByteVector.fromSize(3);
104+
105+
// Act / Assert
106+
assert.throws(() => Id3v2FrameHeader.fromData(testBytes, 4));
107+
}
108+
109+
@test
110+
public fromData_v4_standardIdentifier() {
111+
// Arrange
112+
const testBytes = ByteVector.fromString("TIT1", StringType.Latin1);
113+
114+
// Act
115+
const header = Id3v2FrameHeader.fromData(testBytes, 4);
116+
117+
// Assert
118+
assert.isOk(header);
119+
assert.strictEqual(header.frameId, FrameIdentifiers['TIT1']);
120+
assert.strictEqual(header.flags, 0);
121+
assert.strictEqual(header.frameSize, 0);
122+
}
123+
124+
@test
125+
public fromData_v4_nonstandardIdentifier() {
126+
// Arrange
127+
const testBytes = ByteVector.fromString("NON1", StringType.Latin1);
128+
129+
// Act
130+
const header = Id3v2FrameHeader.fromData(testBytes, 4);
131+
132+
// Assert
133+
assert.isOk(header);
134+
Testers.bvEqual(header.frameId.render(4), testBytes);
135+
assert.strictEqual(header.flags, 0);
136+
assert.strictEqual(header.frameSize, 0);
137+
}
138+
}

0 commit comments

Comments
 (0)