Is Automation 360 safe?

High risk

Automation 360 is high risk. When Automation 360's API recorder starts for a matching tab, it requests cookies for jeckycompay.lightning.force.com and inserts them as a Cookie header in the recorded node, sent to the native agent. The behavioral run skipped this path.…

Automation Anywherev4.4.0.0Chrome Web Store
73Risk
Who publishes it

Automation Anywhere Inc - no other listings under this identity, 1 shared hostname

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Automation Anywhere
Declared legal entity
Automation Anywhere Inc
Registered address
633 River Oaks Pkwy, San Jose, CA 95134-1907, US
Registered contact
Gaurangkumar Patel

Shared hosts - 1 hostname

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

coderepos.org
Also called by 4 other listings, including Automation 360, Automation Anywhere

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Salesforce Cookies Added To API Recordings

When Automation 360's API recorder starts for a matching tab, it requests cookies for jeckycompay.lightning.force.com and inserts them as a Cookie header in the recorded node, sent to the native agent.

The behavioral run skipped this path.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You start API recording for a tab that matches the requested recording URL.

The extension did this

The extension adds cookies from a fixed Salesforce org to the recorded API node and sends that node to the native automation agent.

Fields added to the recorded API node
  • Fixed Salesforce origin
    https://jeckycompay.lightning.force.com/

    This is the Salesforce site whose cookies are requested, even though the recorded request is selected through the API recorder workflow.

  • Salesforce cookie header
    sid=00D5g00000AbCDe!AQEAQEJ2zV8gF9v3kZ7YpR1nM6bH4qT0;inst=APP_005 (illustrative)

    This can identify an authenticated Salesforce browser session for that org when the browser has matching cookies.

  • Recorded request metadata
    method=POST; url=https://jeckycompay.lightning.force.com/services/data/v59.0/query/ (illustrative)

    The API recorder keeps the request ID, method, URL, content type, headers, and post data when present.

  • Native message wrapper
    apiResponse.headers contains name=Cookie and the Salesforce cookie string

    The node is wrapped as an API recorder response and sent to the local automation agent connection.

Recorder workflow from user action to native-agent output
  1. on start

    You did

    You start API recording for a selected web application tab.

    The extension did

    The extension searches open windows for a tab whose URL starts with the requested recording URL.

  2. same workflow

    You did

    A matching tab is found and activated.

    The extension did

    The extension attaches the network debugger and begins receiving request events.

  3. per request

    You did

    A matching network request is observed during recording.

    The extension did

    The extension requests cookies for the fixed Salesforce origin and appends them as a Cookie header.

  4. after assembly

    You did

    The recorded node is assembled.

    The extension did

    The extension posts the API response object to the native automation agent port.

The code that does this

The recorder path that adds Salesforce cookies

Readable version

API recorder setup

ExtensionRecorderApiAction.js
async function registerApiRecorderEvent(context, request) {    context.apiRecorder.jsonMessage = request;    context.apiRecorder.rawMessage = request;    var windows = undefined;    try {        windows = await retrieveAllWindows();        await onAllWindowCallBack(windows, context);    } catch (e) {        AALogger.warn('script', e);        context.sendResponse(getErrorResponse(e.message));        return;    }}async function onAllWindowCallBack(windows, context) {    for (var windowIndex = 0; windowIndex < windows.length; windowIndex++) {        for (var tabIndex = 0; tabIndex < windows[windowIndex].tabs.length; tabIndex++) {            var tabAtIndex = windows[windowIndex].tabs[tabIndex];            if (tabAtIndex.url && tabAtIndex.url.indexOf(context.apiRecorder.jsonMessage.startRecording.url) == 0) {                AALogger.log('ExtensionAPIAction', 'Url is matched.');                var tabs = await updateTab(tabAtIndex.id, { "active": true });                await onTabActive(tabs, context);                return;            }        }    }    sendErrorResponse(createErrorMessage("Url does not match."), context);}async function onTabActive(tab, context) {    AALogger.log('ExtensionAPIAction', 'tab is active.');    var tab = await getCurrentWindowActiveTab(tab.windowId);    await queryCallBack(tab, context);}async function queryCallBack(tab, context) {    try {        if (tab == undefined) {            AALogger.log('ExtensionAPIAction', 'No tab is active.');            var errorResponse = createErrorMessage("No active tab found.");            sendErrorResponse(errorResponse, context)            return;        }        context.apiRecorder.tab = tab;        var tab = tab;        AALogger.log('ExtensionAPIAction', "TabId: " + tab.id);        var contextMessage = context.apiRecorder.jsonMessage;        if (contextMessage.startRecording) {            await attachDebuggerForAPIRecorder(tab, context);            sendStartRecorderResponse(tab, context);        }        AALogger.log('ExtensionAPIAction', 'Response Sent to browser agent');        chrome.tabs.sendMessage(tab.id, context.apiRecorder.rawMessage, function (response) {            registerUserActionEvent(context);        });        AALogger.log('ExtensionAPIAction', 'message sent to content script');    } catch (e) {        AALogger.log('ExtensionAPIAction', e.message);        sendErrorResponse(createErrorMessage(e.message), context)    }}async function attachDebuggerForAPIRecorder(requestFilter, context) {    var tab = context.apiRecorder.tab;    var debuggerRequest = {        tabId: parseInt(tab.id, 10),        debuggerAction: 'NetworkEnable'    };    await sendCommandForNetworkDebugger(debuggerRequest, (result) => ({}));    context.registerListenerDuringRequest(chrome.debugger.onEvent, onAPIRequestSent);    context.apiRecorder.isStarted = true;}

Network request handler

ExtensionRecorderApiAction.js
async function onAPIRequestSent(context, source, method, params) {    var defaultContentType = ["application/json", "application/x-www-form-urlencoded", "application/xml", "text/plain", "text/xml", "text/html", "multipart/form-data"];    var contentTypeMapper = {        "application/x-www-form-urlencoded": "APPLICATION_FORM",        "application/json": "APPLICATION_JSON",        "application/xml": "APPLICATION_XML",        "text/plain": "PLAIN_TEXT",        "text/xml": "TEXT_XML",        "text/html": "TEXT_HTML",        "multipart/form-data": "MULTIPART_FORM"    }    var valueDictionary = [];    if (method === "Network.requestWillBeSentExtraInfo") {        if (params.headers) {            var KEY_METHOD = ':method';            var KEY_SCHEMA = ':scheme';            var KEY_AUTHORTY = ':authority';            var KEY_PATH = ':path';            var KEY_CONTENT_TYPE = 'content-type';            var requestMethod = params.headers[KEY_METHOD];            if (requestMethod === "HEAD") return;            var schema = params.headers[KEY_SCHEMA];            var authority = params.headers[KEY_AUTHORTY];            var path = params.headers[KEY_PATH];            if (!(schema || authority || path)) return;            var requestUrl = schema + "://" + authority + path;            if (requestUrl.indexOf(context.apiRecorder.jsonMessage.startRecording.url) === -1) return;            var node = { "requestId": params.requestId, "method": requestMethod, "url": requestUrl, "authentication": "NoAuthentication" };            //decide the control type            var contentType = params.headers[KEY_CONTENT_TYPE];            if (!contentType) contentType = params.headers['accept'];            if (contentType) {                var contentTypeSegment = contentType.split(";")                var parameterName = '';                if (defaultContentType.indexOf(contentTypeSegment[0]) >= 0) {                    var contentType = { "type": contentTypeMapper[contentTypeSegment[0]] };                    node.contentType = contentType;                    parameterName = contentTypeSegment[0];                } else {                    var contentType = { "type": "CUSTOM", "value": contentTypeSegment[0] };                    node.contentType = contentType;                    parameterName = "custom";                }            }            for (var key in params.headers) {                if (key === KEY_METHOD || key === KEY_SCHEMA || key == KEY_AUTHORTY || key === KEY_CONTENT_TYPE || key === KEY_PATH) continue;                valueDictionary.push({ "name": key, "value": params.headers[key] });            }            node.headers = valueDictionary;            var debuggerRequest = {                tabId: parseInt(context.apiRecorder.tab.id, 10),                debuggerAction: 'NetworkPostData',                commandData: { requestId: params.requestId }            };            //await executeDebuggerCommand(context, debuggerRequest, (result) => ({node: {body: result}}));            await sendCommandForNetworkDebugger(debuggerRequest, function (scriptResponse) {                if (scriptResponse && scriptResponse.postData) {                    node.body = JSON.stringify(scriptResponse);                }                sendApiResponse(node, context);            });        }    } else if (method === "Network.requestWillBeSent") {        //if(params.request.url.indexOf("aura.RecordUi.createRecord") === -1) return;        if (params.redirectHasExtraInfo === true) return;        if (params.request.url.indexOf(context.apiRecorder.jsonMessage.startRecording.url) === -1) return;        if (params.request && params.request.headers) {            var node = { "requestId": params.requestId, "method": params.request.method, "url": params.request.url, "authentication": "NoAuthentication" };            var contentType = params.request.headers['Content-Type'];            if (!contentType) contentType = params.request.headers['accept'];            if (contentType) {                var contentTypeSegment = contentType.split(";")                var parameterName = '';                if (defaultContentType.indexOf(contentTypeSegment[0]) >= 0) {                    var contentType = { "type": contentTypeMapper[contentTypeSegment[0]] };                    node.contentType = contentType;                    parameterName = contentTypeSegment[0];                } else {                    var contentType = { "type": "CUSTOM", "value": contentTypeSegment[0] };                    node.contentType = contentType;                    parameterName = "custom";                }            }            for (var key in params.request.headers) {                if (key === 'Content-Type') continue;                valueDictionary.push({ "name": key, "value": params.request.headers[key] });            }            var debuggerRequest = {                tabId: parseInt(context.apiRecorder.tab.id, 10),                debuggerAction: 'GetCookies',                commandData: { "urls": ['https://jeckycompay.lightning.force.com/'] }            };            var result = await sendCommandForNetworkDebugger(debuggerRequest);            var cookieHeader = ""            for (var index = 0; index < result.cookies.length; index++) {                if (cookieHeader !== "") cookieHeader = cookieHeader + ";"                cookieHeader = cookieHeader + result.cookies[index].name + "=" + result.cookies[index].value;            }            valueDictionary.push({ "name": "Cookie", "value": cookieHeader });            node.headers = valueDictionary;            if (params.request.hasPostData === true) {                // var decodedData = decodeURIComponent(params.request.postData);                // var urlParams = new URLSearchParams(params.request.postData);                // var allParams = urlParams.keys();                // for (const key of allParams) {                // var keyValue = urlParams.get(key);                // //const messageObj = JSON.parse(keyValue);                // console.log(key);                // console.log(keyValue);                // }                node.body = JSON.stringify(params.request.postData);            }            sendApiResponse(node, context);        }    }}

Debugger command wrapper

ExtensionDebuggerCommand.js
async function sendCommandForNetworkDebugger(debuggerCommand, resultCallback) {    var target = { tabId: debuggerCommand.tabId };    try {        var commandResult;        if (debuggerCommand.debuggerAction === "NetworkEnable") {            await chrome.debugger.attach(target, "1.3");            commandResult = await chrome.debugger.sendCommand(target, "Network.enable");        } else if (debuggerCommand.debuggerAction === "NetworkDisable") {            commandResult = await chrome.debugger.sendCommand(target, "Network.disable");            await chrome.debugger.detach(target);        } else if (debuggerCommand.debuggerAction === "NetworkPostData") {            commandResult = await chrome.debugger.sendCommand(target, "Network.getRequestPostData", debuggerCommand.commandData);        } else if (debuggerCommand.debuggerAction === "GetCookies") {            commandResult = await chrome.debugger.sendCommand(target, "Network.getCookies", debuggerCommand.commandData);        }        if (resultCallback)            resultCallback(commandResult);        else            return commandResult;    } catch (e) {        AALogger.warn('debugger', 'err', e);        chrome.debugger.detach(target, debuggerCommand.detachCallback);    }}

Native-port response path

ExtensionRecorderApiAction.js / BrowserNativeMessaging.js
function sendApiResponse(message, context) {    var response = { "apiResponse": message };    context._port.postMessage(response);}function BrowserNativePort(extensionId) {    var np = this;    var port = chrome.runtime.connectNative(extensionId);    this.onMessage = new ListenableEvent("onMessage");    this.onDisconnect = new ListenableEvent("onDisconnect");    port.onDisconnect.addListener((event) => {        var results = np.onDisconnect.fire(event);        if (results && results.length > 0) {            return results[0];        }    })    port.onMessage.addListener((event) => {        var results = np.onMessage.fire(event);        if (results && results.length > 0) {            return results[0];        }    })    this.postMessage = function(message, responseFunc) {        return port.postMessage(message, responseFunc);    }    this.disconnect = function() {        return port.disconnect();    }}
Destinations and fixed origins referenced by this path
    • jeckycompay.lightning.force.com

    Fixed Salesforce org URL used for the debugger cookie lookup.

    • automation.edge.browseragent

    Native-messaging host name for the local Automation 360 browser agent that receives apiResponse messages.

What it can do

Permissions this extension asks for, as declared in version 4.4.0.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    <all_urls>

  • Store data in your browser

    storage

  • See the address and title of every tab you have open

    tabs

  • See every page you navigate to, as you navigate to it

    webNavigation

  • Run its own code inside the pages you visit

    scripting

  • Talk to a program installed on your computer, outside the browser's sandbox

    nativeMessaging

  • Attach to pages with the browser's debugger, which can read and rewrite anything on them

    debugger

  • Schedule its own background tasks

    alarms

Updated 30 September 2026bboccnojhhoegfffpcllbeigipedjedf