Is Numeo Spot safe?

High risk

Numeo Spot is high risk. Once the WEX FleetOne flow reaches a FleetDocs page, it asks Chrome for FleetOne cookies, selects the WEX auth/session ones, adds the page's account label, and sends it to Numeo's WEX cookies endpoint with the user's bearer token.

gcp-developersv3.6.20Chrome Web Store
72Risk
Who publishes it

gcp-developers - 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
gcp-developers

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.

mytriumph.com
Also called by 4 other listings, including LoadConnect, Loadex

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

Publishers can request a review.

Findings

WEX FleetOne cookies forwarded after login

Once the WEX FleetOne flow reaches a FleetDocs page, it asks Chrome for FleetOne cookies, selects the WEX auth/session ones, adds the page's account label, and sends it to Numeo's WEX cookies endpoint with the user's bearer token.

Severity
High unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

You complete the WEX FleetOne connection flow through the extension.

The content script waits for an extension session flag before starting cookie detection on FleetOne pages.

The extension did this

The extension collects selected FleetOne cookies and forwards them to Numeo with the account label.

The code requires both FDWEXAUTH and FleetDocs_SID before sending the message that triggers the backend POST.

Fields the code places into the WEX cookie submission
  • WEX authentication cookie
    FDWEXAUTH=8B7F6C2E4F1A9D0C5B3A21 (illustrative)

    This can identify an authenticated FleetOne browser session after you sign in.

  • FleetDocs session cookie
    FleetDocs_SID=6f8c2e91-5b7a-4d40-a745-8267cfd93211 (illustrative)

    This adds session context for the FleetDocs part of your WEX account.

  • Security and routing cookies
    AWSALB=QmFzZTY0TG9hZEJhbGFuY2VyVmFsdWU= (illustrative)

    These values describe the browser session state around the FleetOne login.

  • Account label from the page
    ACME Logistics LLC (illustrative)

    This ties the cookie submission to the account name or identifier visible on the FleetDocs page.

  • Numeo bearer token
    Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 (illustrative)

    This authenticates the extension's request to Numeo while the FleetOne cookie data is submitted.

Captured request
POSThttps://api.numeo.ai/v1/factoring/wex/cookies
Headers
Accept
application/json
Authorization
Bearer <redacted>
Extension-Version
3.6.1
The code that does this

The FleetOne content script filters cookies, then the background listener posts them

Readable version

wexContent.ts extractAndSendWexCookies

src/content/wexContent.ts
async function extractAndSendWexCookies() {    if (cookiesDetected) {        return    }    try {        const parsedDocCookies: Record<string, unknown>[] = []        if (document.cookie) {            const cookiePairs = document.cookie.split(';')            cookiePairs.forEach((pair) => {                const [name, value] = pair.trim().split('=')                if (name && value) {                    parsedDocCookies.push({                        name: name.trim(),                        value: value.trim(),                        domain: window.location.hostname,                        path: '/',                    })                }            })        }        chrome.runtime.sendMessage(            {                type: 'EXTRACT_WEX_COOKIES_FROM_BACKGROUND',                url: window.location.href,            },            async (response) => {                if (chrome.runtime.lastError) {                    const lastError = chrome.runtime.lastError                    log.e('WEX: Error requesting cookies from background:', toError(lastError))                    return                }                if (!response || !response.success) {                    log.e('WEX: Failed to get cookies:', toError(response?.error))                    return                }                const cookiesFromBackground = response.cookies                const mergedCookiesMap = new Map()                parsedDocCookies.forEach((c) => {                    mergedCookiesMap.set(c.name, c)                })                cookiesFromBackground.forEach((c: Record<string, unknown>) => {                    if (!mergedCookiesMap.has(c.name as string)) {                        mergedCookiesMap.set(c.name as string, c)                    }                })                const cookies = Array.from(mergedCookiesMap.values())                const criticalCookieNames = ['FDWEXAUTH', 'FleetDocs_SID']                const securityCookieNames = ['reese84']                const sessionCookieNames = ['visid_incap_1686407', 'AWSALB', 'AWSALBCORS']                const wexCookies = cookies.filter((cookie: Record<string, unknown>) => {                    const cookieName = cookie.name as string                    if (criticalCookieNames.includes(cookieName)) return true                    if (securityCookieNames.includes(cookieName)) return true                    if (sessionCookieNames.includes(cookieName)) return true                    if (cookieName.startsWith('incap_ses_')) return true                    if (cookieName.startsWith('nlbi_')) return true                    return false                })                if (wexCookies.length === 0) {                    return                }                const hasFDWEXAUTH = wexCookies.some((c: Record<string, unknown>) => c.name === 'FDWEXAUTH')                const hasFleetDocsSID = wexCookies.some((c: Record<string, unknown>) => c.name === 'FleetDocs_SID')                if (!hasFDWEXAUTH || !hasFleetDocsSID) {                    log.e('WEX: Missing critical cookies (FDWEXAUTH or FleetDocs_SID)', new Error('Missing critical cookies (FDWEXAUTH or FleetDocs_SID)'))                    return                }                cookiesDetected = true                const formattedCookies = wexCookies.map((cookie: Record<string, unknown>) => ({                    name: cookie.name,                    value: cookie.value,                    domain: cookie.domain || '.fleetone.com',                    path: cookie.path || '/',                    expiresAt: cookie.expirationDate                        ? new Date((cookie.expirationDate as number) * 1000).toISOString()                        : new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),                }))                const accountId = await waitForAccountId()                if (!accountId) {                    log.e('WEX: Failed to get accountId from h2 element', new Error('AccountId not found'))                }                chrome.runtime.sendMessage(                    {                        type: 'WEX_COOKIES_DETECTED',                        data: {                            cookies: formattedCookies,                            url: window.location.href,                            timestamp: new Date().toISOString(),                            accountId: accountId || undefined,                        },                    },                    (submitResponse: Record<string, unknown>) => {                        if (chrome.runtime.lastError) {                            const lastError = chrome.runtime.lastError                            log.e('WEX: Error sending cookies to backend:', toError(lastError))                            return                        }                        if (submitResponse?.status === 'success') {                            cleanup()                            StorageUtils.remove('wexExtensionSession' as unknown as string)                                .then(() => {})                                .catch((error) => {                                    log.e('WEX: Failed to clear extension session:', error)                                })                            chrome.runtime.sendMessage({ type: 'CLOSE_CURRENT_TAB' }, (closeResponse) => {                                if (chrome.runtime.lastError) {                                    log.e('WEX: Chrome runtime error when closing tab:', toError(chrome.runtime.lastError))                                    window.close()                                } else if (closeResponse?.status === 'success') {                                } else {                                    log.w('WEX: Unexpected close response, trying window.close():', closeResponse)                                    window.close()                                }                            })                        } else {                            log.e('WEX: Failed to send cookies to backend:', toError(submitResponse))                        }                    }                )            }        )    } catch (error) {        log.e('WEX: Error in extractAndSendWexCookies:', toError(error))    }}

WexListener.ts handleCookiesDetected

src/background/listeners/WexListener.ts
private async handleCookiesDetected(message: WexCookiesDetectedMessage, sendResponse: (response: unknown) => void): Promise<void> {    try {        const criticalCookies = ['FDWEXAUTH', 'FleetDocs_SID']        const providedCookieNames = message.data.cookies.map((c) => c.name)        const missingCriticalCookies = criticalCookies.filter((name) => !providedCookieNames.includes(name))        if (missingCriticalCookies.length > 0) {            sendResponse({ status: 'error', message: `Missing critical cookies: ${missingCriticalCookies.join(', ')}` })            return        }        const authData = await chrome.storage.local.get([AUTH_STORAGE_KEY])        const token = authData[AUTH_STORAGE_KEY]?.token        if (!token) {            backgroundLogger.e('No authentication token found in storage', new Error('No authentication token found'))            sendResponse({ status: 'error', message: 'Not authenticated - no token found' })            return        }        const sessionData = await chrome.storage.local.get(['wexExtensionSession'])        const tokenPurpose = sessionData.wexExtensionSession?.tokenPurpose        try {            await axios.post(                '/factoring/wex/cookies',                {                    cookies: message.data.cookies,                    accountId: message.data.accountId,                    ...(tokenPurpose && { tokenPurpose }),                },                {                    headers: {                        Authorization: `Bearer ${token}`,                    },                }            )            chrome.storage.local.remove(['wexExtensionSession'])            sendResponse({ status: 'success', message: 'Cookies sent to WEX endpoint' })        } catch (fetchError: unknown) {            const axiosError = fetchError as AxiosError<{ message?: string }>            const errorMessage = axiosError.response?.data?.message || axiosError.message            const status = axiosError.response?.status            backgroundLogger.e(`Failed to send cookies to WEX endpoint, status: ${status}`, fetchError as Error)            sendResponse({ status: 'error', message: `Failed to send cookies: ${status} - ${errorMessage}` })        }    } catch (error) {        backgroundLogger.e('Unexpected error handling WEX cookies:', error as Error)        sendResponse({ status: 'error', message: `Failed to process cookies: ${(error as Error).message}` })    }}

background index.ts EXTRACT_WEX_COOKIES_FROM_BACKGROUND handler

src/background/index.ts
if (message.type === 'EXTRACT_WEX_COOKIES_FROM_BACKGROUND') {    const cookiesByUrl = chrome.cookies.getAll({ url: message.url })    const cookieStoresPromise = chrome.cookies.getAllCookieStores()    const allCookiesPromises = [        cookiesByUrl,        cookieStoresPromise.then(async (stores) => {            const allStoreCookies = await Promise.all(stores.map((store) => chrome.cookies.getAll({ url: message.url, storeId: store.id })))            return allStoreCookies.flat()        }),    ]    Promise.all(allCookiesPromises)        .then((cookieArrays) => {            const allCookies = cookieArrays.flat()            const uniqueCookies = Array.from(new Map(allCookies.map((c) => [c.name + c.domain + (c.storeId || ''), c])).values())            sendResponse({                success: true,                cookies: uniqueCookies,            })        })        .catch((error) => {            backgroundLogger.e('Error getting WEX cookies:', error)            sendResponse({                success: false,                error: error instanceof Error ? error.message : 'Failed to get cookies',            })        })    return true}
External service receiving the FleetOne cookie submission
    • api.numeo.ai

    Receives the WEX cookie submission at /v1/factoring/wex/cookies according to the extension source.

    • agent.numeo.ai

    The extension's production web app domain and source of the WEX login flow that sets the session flag.

Updated 30 September 2026mfhpdapldnbmejcanhlbbmjlojncicfj