Is Opus Advisor safe?
Opus Advisor is medium risk. The shipped code builds a completion record from the active tab's URL and title, then POSTs it to api.internal.opuseps.com/v2/lrs/completed_test. The body is drawn from source; testing never reached the login flow.
Who publishes itEpilogue Systems, Inc. - 1 other listing from the same operator, none carrying a finding
Epilogue Systems, Inc. - 1 other listing from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
1 other listing published from this account, 4k+ users between them, none of them carrying a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Active tab URL sent on tutorial completion
The shipped code builds a completion record from the active tab's URL and title, then POSTs it to api.internal.opuseps.com/v2/lrs/completed_test.
The body is drawn from source; testing never reached the login flow.
You close or complete a Follow Me tutorial.
That action reaches the background completion handler after the page script sends the close message.
The extension reads the current active tab and adds its URL and title to the completion record.
The record is then posted to the Opus Advisor API with user and tenant fields from local extension storage.
| Field | Value | Why it matters | |
|---|---|---|---|
Active tab URL | https://intranet.example.com/workflows/quarter-close (illustrative) | Shows which page was active in your browser when the tutorial completion record was prepared. | |
Page title | Quarter Close Workflow - Finance Portal (illustrative) | Adds readable page context next to the URL, which can reveal what you were viewing. | |
User name | alex.lee@example.com (illustrative) | Links the completion record to the signed-in extension user. | |
User account ID | 48291 (illustrative) | Links the record to an internal account identifier for the extension service. | |
Tenant name and ID | Acme Operations / 1173 (illustrative) | Associates the completion event with your organization or workspace in the extension service. | |
Tutorial document | Invoice Approval Walkthrough / 9247 (illustrative) | Shows which tutorial was completed alongside the page that was active at the time. |
| source | web-recorder |
| content-type | application/json |
| authorization | extension auth token from chrome.storage.sync |
The completion path reads the active tab and posts it to the API
function closeFollowmeMode() {
minimizePlayer();
removeTimelineToggleButton();
chromeRuntimeSendMessage("close-followme-mode");
}if (request.msg === "close-followme-mode") {
(async () => {
isFMCompletePending = true;
documentInfo.document.id = playerState.document.id;
documentInfo.document.title = playerState.document.title;
await SendCompleteDocumentStatementAsync();
resetPlayerState();
})();
}async function SendCompleteDocumentStatementAsync() {
const initTime = new Date();
const userDetailsResult = await chrome.storage.local.get("current_user_profile");
const userDetails = userDetailsResult.current_user_profile;
const docId = playerState.document.id;
const docName = playerState.document.title;
const stringifiedPlayerState = JSON.stringify(playerState);
chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
let caption = "";
let url = "";
if (tabs.length > 0) {
activeTabId = tabs[0].id;
caption = tabs[0].title;
url = tabs[0].url;
addFMCompleteDialog(activeTabId, stringifiedPlayerState);
}
let results = await chrome.storage.local.get("stepsContextMatchingStatus");
results = results.stepsContextMatchingStatus;
const totalSteps = results.length;
const matchedSteps = results.filter((value) => value).length;
const scaled = matchedSteps / totalSteps;
const data = {
document: {
docType: 8,
id: docId,
name: docName,
},
result: {
max: totalSteps,
min: 0,
raw: matchedSteps,
completion: true,
scaled: scaled,
},
userId: userDetails.id,
userName: userDetails.loginName,
tenancyId: userDetails.tenantId,
tenancyName: userDetails.tenant.tenancyName,
verb: "completed",
duration: new Date().getTime() - initTime.getTime(),
initTime: initTime,
caption: caption,
url: url,
contextRegistrationId: "",
};
doPostV2("/lrs/completed_test", data);
});
}async function doPostV2(url, body, accessToken = null) {
const token = accessToken || (await chrome.storage.sync.get("auth_token")).auth_token;
const response = await fetch(`https://api.internal.opuseps.com/v2${url}`, {
headers: {
accept: "application/json, text/plain, */*",
authorization: token,
"content-type": "application/json",
source: "web-recorder",
},
body: JSON.stringify(body),
method: "POST",
mode: "cors",
credentials: "include",
});
return response.json();
}- api.internal.opuseps.com
Receives the Follow Me tutorial completion POST at /v2/lrs/completed_test, including the active tab URL and title assembled by background.js.
What it can do
Permissions this extension asks for, as declared in version 2.54.0. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 2.55.0, which we have not unpacked yet.
Read and change your data on every secure site you visit
https://*/*
Read and change your data on every site you visit
http://*/*
Store data in your browser
storage
Act on the current tab, but only after you click the extension
activeTab
Run its own code inside the pages you visit
scripting
See the address and title of every tab you have open
tabs
See, disable and uninstall your other extensions, including your security ones
management