Skip to content

Commit 10182cc

Browse files
committed
Reject unsupporteed transmission medium for a=t and a=T
1 parent 3c7f313 commit 10182cc

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

addons/addon-image/src/kitty/KittyGraphicsHandler.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,9 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
327327
switch (action) {
328328
case KittyAction.TRANSMIT: {
329329
const result = this._handleTransmit(cmd, bytes, decodeError);
330-
if (cmd.id !== undefined) {
330+
// Only send response when _handleTransmit didn't already respond
331+
// (it handles unsupported transmission medium responses internally)
332+
if ((cmd.transmission ?? 'd') === 'd' && cmd.id !== undefined) {
331333
if (decodeError) {
332334
this._sendResponse(cmd.id, 'EINVAL:invalid base64 data', cmd.quiet ?? 0);
333335
} else if (bytes.length > 0) {
@@ -364,6 +366,13 @@ export class KittyGraphicsHandler implements IApcHandler, IResetHandler {
364366
// 2. For t=f/t/s: decode bytes as UTF-8 string (the path/name), then read file contents
365367
// 3. For t=d: treat bytes as image data (current behavior)
366368
// When implementing, also update _handleQuery to accept these transmission mediums.
369+
const transmission = cmd.transmission ?? 'd';
370+
if (transmission !== 'd') {
371+
if (cmd.id !== undefined) {
372+
this._sendResponse(cmd.id, 'EINVAL:unsupported transmission medium', cmd.quiet ?? 0);
373+
}
374+
return true;
375+
}
367376

368377
if (decodeError || bytes.length === 0) return true;
369378

addons/addon-image/test/KittyGraphics.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,114 @@ test.describe('Kitty Graphics Protocol', () => {
748748
const response = await ctx.page.evaluate('window.kittyResponse');
749749
strictEqual(response, '\x1b_Gi=204;OK\x1b\\');
750750
});
751+
752+
// TODO: When file-based transmission mediums (t=f, t=t, t=s) are supported,
753+
// update these tests to verify successful transmission instead of EINVAL.
754+
test('transmit rejects t=f with id (EINVAL response)', async () => {
755+
await ctx.page.evaluate(() => {
756+
(window as any).kittyResponse = '';
757+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
758+
});
759+
760+
await ctx.proxy.write(`\x1b_Gi=300,a=t,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
761+
await timeout(100);
762+
763+
const response: string = await ctx.page.evaluate('window.kittyResponse');
764+
strictEqual(response.startsWith('\x1b_Gi=300;EINVAL:'), true);
765+
});
766+
767+
test('transmit rejects t=s with id (EINVAL response)', async () => {
768+
await ctx.page.evaluate(() => {
769+
(window as any).kittyResponse = '';
770+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
771+
});
772+
773+
await ctx.proxy.write(`\x1b_Gi=301,a=t,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
774+
await timeout(100);
775+
776+
const response: string = await ctx.page.evaluate('window.kittyResponse');
777+
strictEqual(response.startsWith('\x1b_Gi=301;EINVAL:'), true);
778+
});
779+
780+
test('transmit rejects t=t with id (EINVAL response)', async () => {
781+
await ctx.page.evaluate(() => {
782+
(window as any).kittyResponse = '';
783+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
784+
});
785+
786+
await ctx.proxy.write(`\x1b_Gi=302,a=t,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
787+
await timeout(100);
788+
789+
const response: string = await ctx.page.evaluate('window.kittyResponse');
790+
strictEqual(response.startsWith('\x1b_Gi=302;EINVAL:'), true);
791+
});
792+
793+
test('transmit rejects t=f without id (no response)', async () => {
794+
await ctx.page.evaluate(() => {
795+
(window as any).kittyResponse = '';
796+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
797+
});
798+
799+
await ctx.proxy.write(`\x1b_Ga=t,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
800+
await timeout(100);
801+
802+
const response: string = await ctx.page.evaluate('window.kittyResponse');
803+
strictEqual(response, '');
804+
});
805+
806+
// TODO: When file-based transmission mediums (t=f, t=t, t=s) are supported,
807+
// update these tests to verify successful transmit+display instead of EINVAL.
808+
test('transmit+display rejects t=f with id (EINVAL response)', async () => {
809+
await ctx.page.evaluate(() => {
810+
(window as any).kittyResponse = '';
811+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
812+
});
813+
814+
await ctx.proxy.write(`\x1b_Gi=310,a=T,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
815+
await timeout(100);
816+
817+
const response: string = await ctx.page.evaluate('window.kittyResponse');
818+
strictEqual(response.startsWith('\x1b_Gi=310;EINVAL:'), true);
819+
});
820+
821+
test('transmit+display rejects t=s with id (EINVAL response)', async () => {
822+
await ctx.page.evaluate(() => {
823+
(window as any).kittyResponse = '';
824+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
825+
});
826+
827+
await ctx.proxy.write(`\x1b_Gi=311,a=T,t=s,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
828+
await timeout(100);
829+
830+
const response: string = await ctx.page.evaluate('window.kittyResponse');
831+
strictEqual(response.startsWith('\x1b_Gi=311;EINVAL:'), true);
832+
});
833+
834+
test('transmit+display rejects t=t with id (EINVAL response)', async () => {
835+
await ctx.page.evaluate(() => {
836+
(window as any).kittyResponse = '';
837+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
838+
});
839+
840+
await ctx.proxy.write(`\x1b_Gi=312,a=T,t=t,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
841+
await timeout(100);
842+
843+
const response: string = await ctx.page.evaluate('window.kittyResponse');
844+
strictEqual(response.startsWith('\x1b_Gi=312;EINVAL:'), true);
845+
});
846+
847+
test('transmit+display rejects t=f without id (no response)', async () => {
848+
await ctx.page.evaluate(() => {
849+
(window as any).kittyResponse = '';
850+
(window as any).term.onData((data: string) => { (window as any).kittyResponse = data; });
851+
});
852+
853+
await ctx.proxy.write(`\x1b_Ga=T,t=f,f=100;${KITTY_BLACK_1X1_BASE64}\x1b\\`);
854+
await timeout(100);
855+
856+
const response: string = await ctx.page.evaluate('window.kittyResponse');
857+
strictEqual(response, '');
858+
});
751859
});
752860

753861
test.describe('Unimplemented action responses', () => {

0 commit comments

Comments
 (0)