PageSourceSearch

https://edr-module.threatdown.com/RemoteShellHandler-LJwN7Bw6.js

js threatdown.com collected 2026-09-24 17:45:19 UTC 28,408 bytes, 855 lines download raw bytes

1import { E as EnumOf, c as concatBuffer, a as fromByteArray, e as encodeUTF16LE, s as stringToArrayBuffer, t as toByteArray, b as arrayBufferToString, d as digestToHex, g as decodeUTF16LE } from './utils-CRB9uaPI.js';
2
3const newLine = "\n";
4const ActionType = EnumOf({
5  Health: "health",
6  Message: "message"
7});
8const MessageType = EnumOf({
9  Command: "command",
10  Connection: "connection",
11  Download: "download",
12  Get: "get",
13  Put: "put",
14  Response: "response",
15  ResponseAck: "response_ack",
16  Sandbox: "sandbox",
17  Signal: "signal",
18  TransferComplete: "transfer_completed",
19  Upload: "upload"
20});
21const SpecialMessage = EnumOf({
22  CTRL_C: "ctrl_c",
23  Quit: "quit"
24});
25
26const endSessionString = "--- SESSION TERMINATED ---";
27const maxDurationString = "Remote Session duration limit reached (1 hour)";
28const maxWaitingTimeString = "Client connection timeout";
29const healthInterval = 2e3;
30const maxWaitingTime = 3e5;
31const maxSessionTime = 36e5;
32const maxUploadSize = 67108864;
33const BUFFER_CHANGE_EVENT = "buffer-change";
34const INPUT_CHANGE_EVENT = "input-change";
35const CURRENT_FOLDER_CHANGE_EVENT = "current-folder-change";
36const CONNECTED_CHANGE_EVENT = "connected-change";
37const CONNECTING_CHANGE_EVENT = "connecting-change";
38const UPLOADING_CHANGE_EVENT = "uploading-change";
39const DOWNLOADING_CHANGE_EVENT = "downloading-change";
40const CONNECTION_ESTABLISHED_EVENT = "connection-established-change";
41const RESPONSE_FINISHED_EVENT = "response-finished";
42class RemoteShellHandler {
43  authToken;
44  arsBaseUrl;
45  accountId;
46  machineId;
47  machineName;
48  sessionId;
49  userInfo;
50  rsaKeyPair = null;
51  aesKey = null;
52  healthLoop = null;
53  sessionTimeout = null;
54  waitingTimeout = null;
55  ws = null;
56  connected = false;
57  downloading = false;
58  uploading = false;
59  connecting = false;
60  connectionEstablished = false;
61  buffer = "";
62  currentFolder = "";
63  callBacks = null;
64  lastCommand = null;
65  enableCmdInput = true;
66  isInsideAnExecCmd = false;
67  progressLoop = null;
68  progressBaseMessage = "";
69  progressDots = 0;
70  staticBufferSnapshot = "";
71  constructor(arsBaseUrl, accountId, machineId, machineName, userInfo, callbacks) {
72    this.arsBaseUrl = arsBaseUrl;
73    this.accountId = accountId;
74    this.machineId = machineId;
75    this.machineName = machineName;
76    this.userInfo = userInfo;
77    this.callBacks = callbacks;
78  }
79  listeners = {};
80  on(event, callback) {
81    if (!this.listeners[event]) {
82      this.listeners[event] = [];
83    }
84    this.listeners[event].push(callback);
85  }
86  off(event, callback) {
87    if (!this.listeners[event]) return;
88    this.listeners[event] = this.listeners[event].filter((cb) => cb !== callback);
89  }
90  emit(event, data) {
91    if (this.listeners[event]) {
92      this.listeners[event].forEach((callback) => callback(data));
93    }
94  }
95  getMachineName() {
96    return this.machineName;
97  }
98  getMachineId() {
99    return this.machineId;
100  }
101  getUserInfo() {
102    return this.userInfo;
103  }
104  getAccountId() {
105    return this.accountId;
106  }
107  setLastCommand(cmd) {
108    this.lastCommand = cmd;
109  }
110  isInExecMode() {
111    return this.isInsideAnExecCmd;
112  }
113  setCmdInputEnabled(enable) {
114    this.enableCmdInput = enable;
115  }
116  getAESKey() {
117    return this.aesKey;
118  }
119  getSessionId() {
120    return this.sessionId;
121  }
122  isConnected() {
123    return this.connected;
124  }
125  isConnecting() {
126    return this.connecting;
127  }
128  isUploadingFile() {
129    return this.uploading;
130  }
131  isDownloadingFile() {
132    return this.downloading;
133  }
134  getBuffer() {
135    return this.buffer;
136  }
137  getCurrentDirectory() {
138    return this.currentFolder;
139  }
140  updateInputValue(value) {
141    this.emit(INPUT_CHANGE_EVENT, value);
142  }
143  quitArsSession(reason) {
144    this.stopProgress();
145    if (reason) {
146      this.updateBuffer(reason);
147    }
148    this.callBacks?.quitArsSession();
149    this.ws?.close();
150    this.connected = false;
151    this.emit(CONNECTED_CHANGE_EVENT, this.connected);
152    this.connectionEstablished = false;
153    this.emit(CONNECTION_ESTABLISHED_EVENT, this.connectionEstablished);
154    this.emit(CURRENT_FOLDER_CHANGE_EVENT, "");
155  }
156  sendFeedbackToBuffer = (message) => {
157    if (message.public_key) {
158      this.updateProgress("receiving public key");
159    }
160    if (message.handshake_completed) {
161      this.stopProgress("");
162      this.emit(BUFFER_CHANGE_EVENT, "clear");
163      if (!this.connectionEstablished) {
164        this.connectionEstablished = true;
165        this.emit(CONNECTION_ESTABLISHED_EVENT, this.connectionEstablished);
166      }
167    }
168  };
169  sendJsonMessage = (message) => {
170    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
171      this.sendFeedbackToBuffer(message);
172      const msg = JSON.stringify(message);
173      this.uploading = true;
174      this.emit(UPLOADING_CHANGE_EVENT, this.uploading);
175      this.ws.send(msg);
176      this.uploading = false;
177      this.emit(UPLOADING_CHANGE_EVENT, this.uploading);
178    } else {
179      console.warn("Socket not open. Message not sent.");
180    }
181  };
182  showError = (title, errors) => {
183    this.callBacks?.showError(title, errors);
184  };
185  showNotification = (notification) => {
186    this.callBacks?.showNotification(notification);
187  };
188  sendAuditLog = (auditObj) => {
189    this.callBacks?.sendAuditLog(auditObj);
190  };
191  updateBuffer = (value) => {
192    this.buffer = value;
193    this.emit(BUFFER_CHANGE_EVENT, this.buffer);
194    const prompt = this.currentFolder ? this.currentFolder : "> ";
195    this.emit(CURRENT_FOLDER_CHANGE_EVENT, prompt);
196  };
197  setDownloadStarted = (isDownloading) => {
198    this.downloading = isDownloading;
199  };
200  setUploadingStarted = (isUploading) => {
201    this.uploading = isUploading;
202  };
203  startProgress(msg) {
204    this.progressBaseMessage = msg;
205    this.progressDots = 0;
206    if (!this.progressLoop) {
207      this.staticBufferSnapshot = this.buffer;
208      this.progressLoop = setInterval(() => {
209        this.progressDots = (this.progressDots + 1) % 4;
210        const dots = ".".repeat(this.progressDots);
211        this.buffer = this.staticBufferSnapshot + newLine + this.progressBaseMessage + dots;
212        this.emit(BUFFER_CHANGE_EVENT, this.buffer);
213      }, 500);
214    }
215  }
216  updateProgress(msg) {
217    if (this.progressLoop) {
218      this.progressBaseMessage = msg;
219      this.progressDots = 0;
220      this.buffer = this.staticBufferSnapshot + newLine + this.progressBaseMessage;
221      this.emit(BUFFER_CHANGE_EVENT, this.buffer);
222    } else {
223      this.startProgress(msg);
224    }
225  }
226  stopProgress(finalMsg) {
227    if (this.progressLoop) {
228      clearInterval(this.progressLoop);
229      this.progressLoop = null;
230      this.buffer = this.staticBufferSnapshot + (finalMsg ? newLine + finalMsg + newLine : "");
231      this.staticBufferSnapshot = "";
232      this.emit(BUFFER_CHANGE_EVENT, this.buffer);
233    }
234  }
235  downloadUrl = (url, fileName) => {
236    const a = document.createElement("a");
237    document.body.appendChild(a);
238    a.style.display = "none";
239    a.href = url;
240    a.download = fileName || "";
241    a.click();
242    window.URL.revokeObjectURL(url);
243  };
244  handleTimeoutsAndIntervals = () => {
245    if (this.connected && this.ws) {
246      if (this.healthLoop) {
247        clearInterval(this.healthLoop);
248        this.healthLoop = setInterval(() => {
249          this.sendJsonMessage({
250            action: ActionType.Health,
251            session_id: this.sessionId,
252            timestamp: Date.now()
253          });
254        }, healthInterval);
255      }
256      if (this.sessionTimeout) {
257        clearTimeout(this.sessionTimeout);
258        this.sessionTimeout = setTimeout(() => {
259          console.log(maxDurationString);
260        }, maxSessionTime);
261      }
262      if (this.waitingTimeout) {
263        clearTimeout(this.waitingTimeout);
264        this.waitingTimeout = setTimeout(() => {
265          console.log(maxWaitingTimeString);
266        }, maxWaitingTime);
267      }
268    } else {
269      if (this.healthLoop) {
270        clearInterval(this.healthLoop);
271      }
272      if (this.sessionTimeout) {
273        clearTimeout(this.sessionTimeout);
274      }
275      if (this.waitingTimeout) {
276        clearTimeout(this.waitingTimeout);
277      }
278    }
279  };
280  handleConnectedSocket = async () => {
281    if (this.ws && this.sessionId) {
282      if (this.waitingTimeout) {
283        clearTimeout(this.waitingTimeout);
284      }
285      const keyPair = await window.crypto.subtle.generateKey(
286        {
287          hash: "SHA-256",
288          modulusLength: 4096,
289          name: "RSA-OAEP",
290          publicExponent: new Uint8Array([1, 0, 1])
291        },
292        false,
293        ["encrypt", "decrypt"]
294      );
295      this.rsaKeyPair = keyPair;
296      if (keyPair.publicKey) {
297        try {
298          const publicKey = await crypto.subtle.exportKey(
299            "spki",
300            keyPair.publicKey
301          );
302          let body = btoa(
303            String.fromCharCode(...new Uint8Array(publicKey))
304          );
305          body = body.match(/.{1,64}/g).join("\n");
306          this.sendJsonMessage({
307            action: ActionType.Message,
308            public_key: `-----BEGIN PUBLIC KEY-----
309${body}
310-----END PUBLIC KEY-----`,
311            session_id: this.sessionId
312          });
313        } catch (err) {
314          this.showError("Connection error", [
315            { error: { message: `Something went wrong. (${err.message})` } }
316          ]);
317        }
318      }
319    }
320  };
321  putFileRemoteShell = async (url, fileToSend, fileName) => {
322    const headers = new Headers();
323    headers.append("Content-Type", "application/x-binary");
324    headers.append("Content-Disposition", "attachment");
325    const formData = new FormData();
326    formData.append("data", new Blob([fileToSend]), fileName);
327    const response = await fetch(url, {
328      method: "PUT",
329      headers,
330      body: formData
331    });
332    if (response.ok) {
333      return true;
334    }
335    return null;
336  };
337  fetchFile = (url) => fetch(url, {
338    headers: { "Content-Type": "application/x-binary" },
339    method: "GET"
340  });
341  fileTransferRemoteShell = async (param) => {
342    const {
343      authToken,
344      account_id,
345      client_method,
346      client_type,
347      file_name,
348      machine_id,
349      session_id,
350      user_id
351    } = param;
352    const response = await fetch(`${this.arsBaseUrl}/signedurl?client_type=${client_type}`, {
353      method: "POST",
354      headers: {
355        authorization: `Bearer ${authToken}`,
356        "Content-type": "application/json"
357      },
358      body: JSON.stringify({
359        account_id,
360        client_method,
361        file_name,
362        machine_id,
363        session_id,
364        user_id
365      })
366    });
367    if (response.ok) {
368      return await response.json();
369    }
370    return null;
371  };
372  handleFileUpload = async (event) => {
373    const target = event.target;
374    if (target.files?.length && this.aesKey) {
375      this.uploading = true;
376      const currentFile = target.files[0];
377      target.files = null;
378      target.value = "";
379      if (!currentFile) {
380        this.showNotification({
381          message: "File size limit exceeded (64MB).",
382          title: "Error uploading file",
383          type: "critical"
384        });
385        this.uploading = false;
386        return;
387      }
388      const uploadMessage = `${MessageType.Put} "${currentFile?.name}"`;
389      this.updateBuffer(
390        this.buffer + newLine + this.currentFolder + " " + uploadMessage + newLine
391      );
392      this.updateInputValue("");
393      this.sendAuditLog({
394        message: uploadMessage,
395        account_id: this.accountId,
396        connection_start: false,
397        machine_id: this.machineId,
398        pc_hostname: this.machineName,
399        session_id: this.sessionId,
400        user_id: this.userInfo.id || ""
401      });
402      if (currentFile.size > maxUploadSize) {
403        this.showNotification({
404          message: "File size limit exceeded (64MB).",
405          title: "Error uploading file",
406          type: "critical"
407        });
408        this.uploading = false;
409      } else {
410        const params = {
411          authToken: this.authToken || "",
412          account_id: this.accountId,
413          client_method: "put_object",
414          client_type: "web",
415          file_name: currentFile.name,
416          machine_id: this.machineId || "",
417          session_id: this.sessionId || "",
418          user_id: this.userInfo.id
419        };
420        const { presigned_url: presignedUrl } = await this.fileTransferRemoteShell(params);
421        if (!presignedUrl) {
422          this.showNotification({
423            message: "Error during the upload.",
424            title: "Error uploading file",
425            type: "critical"
426          });
427          this.uploading = false;
428          return;
429        }
430        const fileArrayBuffer = await currentFile.arrayBuffer();
431        const digest = await crypto.subtle.digest("SHA-1", fileArrayBuffer);
432        const fileToCrypt = concatBuffer(fileArrayBuffer, digest);
433        const iv = window.crypto.getRandomValues(new Uint8Array(12));
434        const cryptedFile = await window.crypto.subtle.encrypt(
435          {
436            iv,
437            name: "AES-GCM"
438          },
439          this.aesKey,
440          fileToCrypt
441        );
442        const fileToSend = concatBuffer(cryptedFile, iv.buffer);
443        const putFile = await this.putFileRemoteShell(
444          presignedUrl,
445          fileToSend,
446          currentFile.name
447        );
448        this.updateBuffer(this.buffer + newLine + "upload in progress...");
449        if (putFile) {
450          const payloadToCrypt = JSON.stringify({
451            data: fromByteArray(encodeUTF16LE(uploadMessage)),
452            type: MessageType.Upload
453          });
454          const putFileIv = window.crypto.getRandomValues(new Uint8Array(12));
455          const res = await window.crypto.subtle.encrypt(
456            {
457              iv: putFileIv,
458              name: "AES-GCM"
459            },
460            this.aesKey,
461            stringToArrayBuffer(payloadToCrypt)
462          );
463          const encryptedPayload = fromByteArray(
464            new Uint8Array(res.slice(0, -16))
465          );
466          const gcm_tag = fromByteArray(
467            new Uint8Array(res.slice(-16))
468          );
469          this.sendJsonMessage({
470            action: ActionType.Message,
471            body: {
472              gcm_tag,
473              iv: fromByteArray(new Uint8Array(putFileIv)),
474              payload: encryptedPayload
475            },
476            session_id: this.sessionId
477          });
478        }
479      }
480    }
481  };
482  handleMessage = async (messageObj) => {
483    if (!this.aesKey || !messageObj.body) {
484      return;
485    }
486    try {
487      const payloadArray = new Uint8Array(toByteArray(messageObj.body.payload));
488      const tagArray = new Uint8Array(toByteArray(messageObj.body.gcm_tag));
489      const cyphered = new Uint8Array([...payloadArray, ...tagArray]);
490      const decrypted = await crypto.subtle.decrypt(
491        {
492          iv: toByteArray(messageObj.body.iv),
493          name: "AES-GCM",
494          tagLength: 128
495        },
496        this.aesKey,
497        cyphered
498      );
499      const res = await arrayBufferToString(decrypted);
500      const parsedData = JSON.parse(res);
501      const {
502        error,
503        file_name,
504        sandbox_hash,
505        sandbox_information,
506        sandbox_status,
507        success
508      } = parsedData.data;
509      if (parsedData.type === MessageType.TransferComplete) {
510        if (this.downloading) {
511          this.downloading = false;
512          this.emit(DOWNLOADING_CHANGE_EVENT, this.downloading);
513          this.updateInputValue("");
514          if (error) {
515            this.updateBuffer(
516              `${this.buffer}${newLine}Error transferring file${newLine}`
517            );
518            this.showError("Error transferring file", [
519              { error: { message: error || "Something went wrong." } }
520            ]);
521            return;
522          }
523          const params = {
524            authToken: this.authToken || "",
525            account_id: this.accountId,
526            client_method: "get_object",
527            client_type: "web",
528            file_name,
529            machine_id: this.machineId,
530            session_id: this.sessionId || "",
531            user_id: this.userInfo.id
532          };
533          if (file_name) {
534            try {
535              const transferRequest = await this.fileTransferRemoteShell(params);
536              if (!transferRequest || !transferRequest.presigned_url) {
537                this.showError("Error downloading file", [
538                  { error: { message: "Error during the download." } }
539                ]);
540                return;
541              }
542              const { presigned_url: presignedUrl } = transferRequest;
543              const file = await this.fetchFile(presignedUrl);
544              if (!file.ok) {
545                this.updateBuffer(
546                  `${this.buffer}${newLine}Error transferring file${newLine}`
547                );
548                this.showError("Error transferring file", [
549                  { error: { message: "Error during the download." } }
550                ]);
551                return;
552              }
553              const fileAB = await file.arrayBuffer();
554              const currentBuffer = fileAB.slice(0, -12);
555              const currentIv = fileAB.slice(-12);
556              if (this.aesKey) {
557                const decryptedFile = await window.crypto.subtle.decrypt(
558                  {
559                    iv: currentIv,
560                    name: "AES-GCM",
561                    tagLength: 128
562                  },
563                  this.aesKey,
564                  currentBuffer
565                );
566                const currentFile = decryptedFile.slice(0, -20);
567                const receivedDigest = decryptedFile.slice(-20);
568                const currentDigest = await crypto.subtle.digest(
569                  "SHA-1",
570                  currentFile
571                );
572                if (digestToHex(currentDigest) === digestToHex(receivedDigest)) {
573                  const href = window.URL.createObjectURL(new Blob([currentFile]));
574                  this.downloadUrl(href, file_name);
575                } else {
576                  this.updateBuffer(
577                    `${this.buffer}${newLine}Error transferring file${newLine}`
578                  );
579                  this.showError("Error transferring file", [
580                    { error: { message: "Hashes don't match." } }
581                  ]);
582                }
583              }
584            } catch (err) {
585              this.showError("Error downloading/transferring file", [
586                {
587                  error: {
588                    message: `Error during the download. (${err.message})`
589                  }
590                }
591              ]);
592            }
593          }
594        }
595        if (sandbox_hash || sandbox_information || sandbox_status) {
596          this.updateBuffer(
597            `${this.buffer}${newLine}${sandbox_status}${newLine}${sandbox_information}${newLine}${sandbox_hash}`
598          );
599        }
600        if (success) {
601          if (!file_name) {
602            this.updateBuffer(
603              `${this.buffer}${newLine}File successfully uploaded ${newLine}`
604            );
605            this.showNotification({
606              title: `File successfully uploaded`,
607              type: "success"
608            });
609          } else {
610            this.updateBuffer(
611              `${this.buffer}${newLine}File ${file_name} successfully downloaded ${newLine}`
612            );
613            this.showNotification({
614              title: `File ${file_name} successfully downloaded`,
615              type: "success"
616            });
617          }
618        } else {
619          this.updateBuffer(
620            `${this.buffer}${newLine}Error transferring file${newLine}`
621          );
622          this.showError("Error transferring file", [
623            { error: { message: error || "Something went wrong." } }
624          ]);
625        }
626        this.enableCmdInput = true;
627        this.emit(RESPONSE_FINISHED_EVENT, this.enableCmdInput);
628      } else if (parsedData.type === MessageType.Response) {
629        const decodedMessage = decodeUTF16LE(
630          toByteArray(parsedData.data.message)
631        );
632        this.currentFolder = parsedData.data.cwd ? decodeUTF16LE(toByteArray(parsedData.data.cwd)) + ">" : "";
633        this.emit(CURRENT_FOLDER_CHANGE_EVENT, this.currentFolder);
634        this.updateBuffer(this.buffer + decodedMessage);
635        const hasCwd = !!parsedData?.data?.cwd;
636        if (hasCwd) {
637          this.isInsideAnExecCmd = false;
638          this.enableCmdInput = true;
639        } else {
640          if (this.lastCommand?.startsWith("exec") || this.lastCommand?.startsWith("put") || this.lastCommand?.startsWith("get") || this.lastCommand?.startsWith("del") || this.lastCommand?.startsWith("cat") || this.lastCommand?.startsWith("move") || this.lastCommand?.startsWith("reg") || this.lastCommand?.startsWith("sc")) {
641            this.isInsideAnExecCmd = true;
642          }
643          this.enableCmdInput = this.isInsideAnExecCmd;
644        }
645        this.emit(RESPONSE_FINISHED_EVENT, this.enableCmdInput);
646        const payload = JSON.stringify({
647          data: {
648            id: messageObj.id
649          },
650          type: MessageType.ResponseAck
651        });
652        const iv = window.crypto.getRandomValues(new Uint8Array(12));
653        if (this.aesKey) {
654          const encrypted = await window.crypto.subtle.encrypt(
655            {
656              iv,
657              name: "AES-GCM"
658            },
659            this.aesKey,
660            stringToArrayBuffer(payload)
661          );
662          const encryptedPayload = fromByteArray(
663            new Uint8Array(encrypted.slice(0, -16))
664          );
665          const gcm_tag = fromByteArray(
666            new Uint8Array(encrypted.slice(-16))
667          );
668          this.sendJsonMessage({
669            action: ActionType.Message,
670            body: {
671              gcm_tag,
672              iv: fromByteArray(new Uint8Array(iv)),
673              payload: encryptedPayload
674            },
675            session_id: this.sessionId
676          });
677        }
678      }
679    } catch (err) {
680      this.showError("Error in decrypt", err);
681    }
682  };
683  handleSymmetricalKeyDecryption = async (symmetricalKey) => {
684    if (this.rsaKeyPair?.privateKey) {
685      const decrypted = await window.crypto.subtle.decrypt(
686        {
687          name: "RSA-OAEP"
688        },
689        this.rsaKeyPair?.privateKey,
690        toByteArray(symmetricalKey)
691      );
692      const str = await arrayBufferToString(decrypted);
693      const k = str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
694      try {
695        const key = await crypto.subtle.importKey(
696          "jwk",
697          {
698            alg: "A256GCM",
699            ext: false,
700            k,
701            kty: "oct"
702          },
703          { name: "AES-GCM" },
704          false,
705          ["encrypt", "decrypt"]
706        );
707        this.sendJsonMessage({
708          action: ActionType.Message,
709          handshake_completed: {
710            success: true,
711            user_email: this.userInfo.email,
712            user_id: this.userInfo.id,
713            user_name: this.userInfo.name
714          },
715          session_id: this.sessionId
716        });
717        this.aesKey = key;
718        this.sendAuditLog({
719          message: "handleSymmetricalKeyDecryption done",
720          account_id: this.accountId,
721          connection_start: true,
722          machine_id: this.machineId,
723          pc_hostname: this.machineName,
724          session_id: this.sessionId,
725          user_id: this.userInfo.id || ""
726        });
727      } catch (err) {
728        this.showError("Description error", [
729          { error: { message: "Error importing key." } }
730        ]);
731      }
732    }
733  };
734  startARSSession = async (authToken) => {
735    const url = `${this.arsBaseUrl}/start?client_type=web`;
736    this.authToken = authToken;
737    this.connecting = true;
738    this.emit(CONNECTING_CHANGE_EVENT, this.connecting);
739    this.startProgress("connection in progress");
740    try {
741      const response = await fetch(url, {
742        method: "POST",
743        headers: {
744          "Content-type": "application/json; charset=UTF-8",
745          accept: "application/json",
746          authorization: `Bearer ${this.authToken}`
747        },
748        body: JSON.stringify({
749          "account_id": this.accountId,
750          "machine_id": this.machineId
751        })
752      });
753      if (!response.ok) {
754        const errorPayload = await response.json();
755        console.error(`Error! status: ${response.status} ${JSON.stringify(errorPayload)}`);
756        this.connecting = false;
757        this.emit(CONNECTING_CHANGE_EVENT, this.connecting);
758        this.connected = false;
759        this.emit(CONNECTED_CHANGE_EVENT, this.connected);
760        return {
761          error: { message: `${response.status} ${JSON.stringify(errorPayload)}`, code: response.status }
762        };
763      }
764      const result = await response.json();
765      const wsUrl = result["ws_url"];
766      this.sessionId = result["session_id"];
767      this.ws = new WebSocket(encodeURI(`${wsUrl}?client_type=web&session_id=${this.sessionId}`), authToken);
768      await this.startRemoteShell();
769      this.connecting = false;
770      this.emit(CONNECTING_CHANGE_EVENT, this.connecting);
771      this.connected = true;
772      this.emit(CONNECTED_CHANGE_EVENT, this.connected);
773      this.updateProgress("web socket connected");
774      return this.ws;
775    } catch (error) {
776      if (error instanceof Error) {
777        this.connected = false;
778        this.emit(CONNECTED_CHANGE_EVENT, this.connected);
779        this.connecting = false;
780        this.emit(CONNECTING_CHANGE_EVENT, this.connecting);
781        console.error("error during startARSSession: ", error.message);
782        return {
783          error: { message: error.message }
784        };
785      } else {
786        this.connected = false;
787        this.emit(CONNECTED_CHANGE_EVENT, this.connected);
788        this.connecting = false;
789        this.emit(CONNECTING_CHANGE_EVENT, this.connecting);
790        console.error("unexpected error: ", error);
791        return null;
792      }
793    }
794  };
795  startRemoteShell = async () => {
796    const instance = this;
797    try {
798      if (!instance.ws) {
799        return;
800      }
801      instance.ws.onclose = function() {
802        instance.quitArsSession("web socket has been closed!");
803      };
804      instance.ws.onerror = function(event) {
805        instance.quitArsSession(`web socket has been closed due to an error: ${event}`);
806      };
807      instance.ws.onmessage = async function(message) {
808        instance.downloading = true;
809        instance.emit(DOWNLOADING_CHANGE_EVENT, instance.downloading);
810        const messageObj = JSON.parse(message.data);
811        if (messageObj) {
812          if (messageObj.connection) {
813            instance.connecting = false;
814            instance.emit(CONNECTING_CHANGE_EVENT, instance.connecting);
815            instance.updateProgress("initiating handshake");
816            await instance.handleConnectedSocket();
817            instance.connected = true;
818            instance.emit(CONNECTED_CHANGE_EVENT, instance.connected);
819          } else if (messageObj.symmetrical_key) {
820            instance.updateProgress("handling keys");
821            await instance.handleSymmetricalKeyDecryption(messageObj.symmetrical_key);
822          } else if (messageObj.action === "health") {
823            instance.stopProgress();
824            instance.quitArsSession(endSessionString);
825          } else if (messageObj.body) {
826            await instance.handleMessage(messageObj);
827          }
828        }
829        instance.downloading = false;
830        instance.emit(DOWNLOADING_CHANGE_EVENT, instance.downloading);
831      };
832      instance.ws.onopen = function() {
833        instance.handleTimeoutsAndIntervals();
834      };
835    } catch (error) {
836      instance.showError("problem starting Remote Shell", []);
837    }
838  };
839}
840
841const RemoteShellHandler$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
842    __proto__: null,
843    BUFFER_CHANGE_EVENT,
844    CONNECTED_CHANGE_EVENT,
845    CONNECTING_CHANGE_EVENT,
846    CONNECTION_ESTABLISHED_EVENT,
847    CURRENT_FOLDER_CHANGE_EVENT,
848    DOWNLOADING_CHANGE_EVENT,
849    INPUT_CHANGE_EVENT,
850    RESPONSE_FINISHED_EVENT,
851    RemoteShellHandler,
852    UPLOADING_CHANGE_EVENT
853}, Symbol.toStringTag, { value: 'Module' }));
854
855export { ActionType as A, BUFFER_CHANGE_EVENT as B, CONNECTED_CHANGE_EVENT as C, DOWNLOADING_CHANGE_EVENT as D, INPUT_CHANGE_EVENT as I, MessageType as M, RESPONSE_FINISHED_EVENT as R, SpecialMessage as S, UPLOADING_CHANGE_EVENT as U, CONNECTING_CHANGE_EVENT as a, CURRENT_FOLDER_CHANGE_EVENT as b, CONNECTION_ESTABLISHED_EVENT as c, RemoteShellHandler$1 as d, newLine as n };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.