Repository navigation
Releases: ATTACKSimulator/events
Release list
v1.4.0
v1.4.0
Two new events for the ClickFix and OAuth consent attack types.
New
command_copied: on ClickFix pages. When the user clicks an element markeddata-ats-copy-command, the library copies the command to the clipboard and sends the event withtype: "command". The command comes from the attribute value, or from the element's text if the value is empty. The page then redirects toredirectUrl.consent_granted: on OAuth consent pages. When the user clicks an element markeddata-ats-consent, the library sends the event with itsdata-scopesastype(e.g."mail.read files.read"). It blocks the element's own action, so a submit button with the attribute no longer submits its form, then redirects toredirectUrl.- Tutorial: both events get their own tutorial step in all 10 locales. A copied command ends with the malware lesson, granted consent with the permissions lesson.
<button data-ats-copy-command="cmd /c echo verified">Copy</button>
<button data-ats-consent data-scopes="mail.read files.read">Allow</button>Changed
button_clickedignores elements that carrydata-ats-copy-commandordata-ats-consent, so a single click is never recorded twice.data-ignorestill opts an element out.
Internal
- Unit tests now run with Vitest in jsdom (
npm test).
Platform side: red-hafen, the stats writers and the reports count both events as affected. command_copied is recorded as infected damage. consent_granted damage depends on the scopes: mail scopes count as emails, anything else as sensitive.
v1.3.2
Fixed
- Redirects no longer drop query params (notably the tk token).
performRedirect()built the target by concatenating redirectUrl withwindow.location.search. When redirectUrl already carried a query string — e.g. a router link with edu/pill_id/locale — the concatenation produced a second ?, so the appended tk token was folded into the preceding value and lost. The post-click redirect then landed unauthenticated and in the wrong locale. The destination is now assembled with URL + URLSearchParams, preserving params already on redirectUrl and merging the current ones in with &.
v1.3.0
v1.3.0 — Teach users what they missed
Until now, a user who fell for a simulated phishing page was redirected straight to the oops page. They learned that they failed, but not what they should have noticed. This release adds an interactive tutorial that inspects the page they fell for, walks them through the warning signs they missed, and then performs the same redirect.
It is off by default. Existing integrations behave exactly as before.
const ev = new ATSEvents({
apiKey: "YOUR_API_KEY",
tutorial: true,
});
ev.listen();What the tutorial explains
The page is scanned the moment the event fires, and every dangerous item found becomes its own step with the element highlighted in place.
- The address — not a generic warning, but why this URL should have raised suspicion: a raw IP, a lookalike built from special characters, a brand parked in a subdomain, a throwaway TLD, or an address long enough to push the real name out of sight. The part that decides ownership is highlighted, and the readout keeps the deceptive bits other tools hide, such as the
user@hosttrick and non-default ports. - Credential and card forms — recognised from labels, ARIA text and surrounding copy, so forms with no
<form>element and password fields typed astextare still caught. - Downloads — links and buttons that would have delivered a file.
- Links that lie — where the visible text and the real destination differ.
- Pressure tactics — the deadline or account-suspension threat is quoted back to the user in their own language.
- Brand impersonation — when a page presents itself as an organisation the address does not belong to.
- Permissions — microphone, camera, location, notifications, clipboard and browser extensions each get their own explanation of what was just granted.
Brand detection without a list
Rather than shipping a list that would always be out of date, the tutorial works out which organisation a page claims to be by scoring independent signals — the copyright line, og:site_name, logo alt text and file names, inline SVG wordmarks, the title and headings — and only names a brand once they agree. That works for organisations nobody bundled a list for, in any language.
If your campaign already knows what the landing page imitates, send it as ats_brand in the campaign token and that value is used instead of any guess.
Languages
Ten locales, each reviewed by a native speaker: en-US, ro-RO, es-ES, ca-ES, es-419 (also accepted as es-LATAM), pt-BR, pt-PT, fr-FR, de-DE, el-GR. The language comes from the locale option, then the campaign token, then the browser, falling back to en-US.
New options
| Option | Type | Default | Description |
|---|---|---|---|
tutorial |
boolean |
false |
Show the tutorial instead of redirecting immediately |
tutorialSkippable |
boolean |
true |
Show a skip link |
locale |
string |
auto | Force the tutorial language |
Smaller bundle
Source maps were being inlined as base64 into the shipped file, where they accounted for three quarters of every byte. They are now emitted as separate .map files, which browsers only fetch when devtools are open.
| Before | After | |
|---|---|---|
bundle.min.js |
244 KB | 190 KB |
| gzipped | ~73 KB | 61 KB |
The bundle is smaller than the previous release despite carrying the whole feature.
Reliability
The redirect is the one thing that must never be lost, so it is now guaranteed on every path. A throwing event subscriber, a failed page scan, or an overlay that the host page hides or removes all fall back to navigating. Since landing pages are attacker-authored, the overlay is checked after opening and the redirect happens anyway if it is not genuinely on screen.
Toolchain
Dependencies were updated: ESLint 10 (flat config), TypeScript 6,
@typescript-eslint 8, webpack-cli 7 and ua-parser-js 2.
Contributors should note that TypeScript stays on 6.x. TypeScript 7 is the Go rewrite and exposes a different JS API, which ts-loader cannot drive; moving to it would mean switching to a different loader.
There are no API changes for consumers of the package.
v1.2.15
Changed
-
Added per-input deduplication for
input_filled. -
Each input field now sends
input_filledonly once per page session. -
Multiple different input fields can still each send their own event.
-
Later edits to the same input are ignored.
-
Replaced the old debounce-based input handling with explicit deduplication.
-
Removed the unused debounce helper and
shouldDebounceevent contract. -
Updated README documentation for the new
input_filledbehavior. -
Added a retrying event sender.
-
New
EventSendermanages in-memorypending,sending,sent, andfailedqueues. -
sg_event_idis assigned once per queued event and reused across retries. -
Retries transient failures: network errors,
408,429, and5xx. -
Non-retryable
4xxresponses fail immediately. -
Improved remote response handling.
-
Remote.post()now returns response status metadata. -
Invalid JSON responses no longer break response handling.
Notes
- Redirecting events use a single send attempt before navigation to avoid delaying user redirects.
v1.2.14
What's Changed
- Added frontend client IP collection to browser event data.
- Included
data.ipbeforedata.uain event payloads when IP lookup succeeds. - Switched IP lookup to
https://ipinfo.io/ipsince only the IP value is needed. - Added a 1500ms abort timeout and
no-referrerpolicy for the IP lookup. - Preserved event delivery when IP lookup fails or times out.
- Cached browser info lookup in
Managerwhile keeping listener registration and subscriptions synchronous. - Fixed the package
mainentrypoint to point todist/bundle.js. - Cleaned up lint-blocking TypeScript declarations and formatting.
- Regenerated
dist/bundle.jsanddist/bundle.min.js. - Updated the local test page for the current event testing flow.
Compatibility Notes
- Public
ATSEvents.listen()andtrigger()behavior is unchanged. - Events still send without
data.ipifipinfo.iois blocked, unavailable, or times out. - Existing browser, OS, device, CPU, screen size, and UA fields remain in
payload.data.
Validation
- TypeScript check passed.
- Production bundle build passed.
v1.2.13
Bug Fixes
- Fixed memory leak where the manual_page_read event listener could not be properly removed due to using an anonymous function reference
- Manual trigger now correctly bypasses scroll/visibility checks, ensuring page_read fires immediately when dispatched manually
- Removed stray console.log from PageRead initialization
Code Quality
- Formatting and style cleanup across PageRead (consistent spacing, semicolons, brace style)
v1.2.12
fix: resolve event listener memory leaks and add manual trigger support
- Convert event handler methods to arrow functions to fix 'this' binding
- Remove unnecessary .bind(this) calls for cleaner code
- Add support for manual page read triggering via window.isManualTrigger
- Fix event listener cleanup to prevent memory leaks