JavaScript Tag

JavaScript Tag

Install

Insert the following tag in the <head> of your HTML file before your main script:

1
2
3
<script>
   (()=>{"use strict";var e={d:(t,n)=>{for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{factory:()=>O,initChallenges:()=>E,initPhishing:()=>T,runChallenge:()=>_,setUserId:()=>S});const n={integration:{type:"UNKNOWN",version:"0.0.0"}},r={getCatcher:()=>{const e=document.createElement("div");return e.style.width="0px",e.style.height="0px",e.style.display="none",e}},i=e=>new Promise((t=>setTimeout(t,e))),o=()=>{};var l=(e,t,n)=>new Promise(((r,i)=>{var o=e=>{try{s(n.next(e))}catch(e){i(e)}},l=e=>{try{s(n.throw(e))}catch(e){i(e)}},s=e=>e.done?r(e.value):Promise.resolve(e.value).then(o,l);s((n=n.apply(e,t)).next())}));const s="__prt__",a=s+"preload__runners_controller",c=()=>String(Math.random()).replace("0.","");function u(e,t,n){return l(this,null,(function*(){let r=null;const i=new Promise((e=>{r=setTimeout((()=>{setTimeout((()=>{e(null)}),100)}),t)})),o=yield Promise.race([e,i]);if(r&&clearTimeout(r),!o)throw new Error(n);return o}))}const d=function(e){return new Promise((function(t,n){let r=0;e.forEach((i=>{i.then(t).catch((function(t){++r===e.length&&n(t)}))}))}))};function h(){let e=o,t=o;return{promise:new Promise(((n,r)=>{e=n,t=r})),resolve:e,reject:t}}const p=()=>{let e=h(),t=e.resolve,n=e.reject;const r=()=>{t=null,n=null},i={current:e.promise,busy:()=>{t||(e=h(),i.current=e.promise,t=e.resolve,n=e.reject)},ready:e=>{t&&t(e),r()},failed:()=>{n&&n(),r()}};return i},y=e=>({then:t=>{if(t){const n=t(e);return n&&"function"==typeof n.then?n:y(n)}return y(e)}});var m=Object.defineProperty,f=Object.getOwnPropertySymbols,g=Object.prototype.hasOwnProperty,w=Object.prototype.propertyIsEnumerable,v=(e,t,n)=>t in e?m(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,P=(e,t)=>{for(var n in t||(t={}))g.call(t,n)&&v(e,n,t[n]);if(f)for(var n of f(t))w.call(t,n)&&v(e,n,t[n]);return e};const b=(()=>{const e={};return t=>{if(e[t])return e[t];const n=function(e){const t=c(),n=s+t;window[n]={};const r=document.createElement("script");r.src=e,r.dataset.id=t,r.async=!0,document.head.append(r);const i=()=>{r.remove(),delete window[n]};return new Promise(((e,t)=>{r.onload=()=>{if(!window[n].r||"function"!=typeof window[n].r)return t(new Error("Not valid script loaded")),void i();e(window[n].r),i()},r.onerror=()=>{i(),t(new Error("Script load error"))}}))}(t);return e[t]=n,n.catch((()=>{delete e[t]})),n}})(),C=e=>{const t=Date.now(),s={api:p(),load:p(),init:p()},c=e=>()=>s[e].current;let h=c("api"),m=c("init");const f=c("load");let g=()=>f().then(h);const w=(e=>{let t=!0;return(...n)=>{t&&(t=!1,e(...n))}})((n=>{(function(e,t,n,r){return l(this,null,(function*(){const n=[];for(let o=0;o<t;o++)try{return n.push(e(o>0)),yield u(d(n),3e3,r)}catch(e){if(o===t-1)throw e;yield i(100)}throw new Error(r+" ("+t+" attempts)")}))})((n=>{let r=0;return()=>{return i=function*(){++r;const i=Date.now();return(yield b((e||n.url)+"/client/v1.js")).bind(null,{s:i,r,i:t})},new Promise(((e,t)=>{var n=e=>{try{o(i.next(e))}catch(e){t(e)}},r=e=>{try{o(i.throw(e))}catch(e){t(e)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,r);o((i=i.apply(null,null)).next())}));var i}})(n),n.reload?1:3,0,"Timeout").then((e=>t=>{e.reload&&(s.init.busy(),s.api.busy());const n=t();return g=()=>y(n),h=()=>y(n),s.api.ready(n),s.load.ready(),!0})(n)).catch((e=>t=>{const n=t instanceof Error?t.message:"UNKNOWN";s.api.ready((e=>{const t=()=>Promise.resolve(e);return{setUserId:o,runCaptcha:()=>({dispose:o,promise:Promise.reject()}),runChallenge:t,initChallenges:()=>Promise.resolve(t),initPhishing:()=>Promise.resolve(r)}})("[Error] Main script load error "+n)),s.init.ready(),setTimeout((()=>{e.reload=!0,v(e)}),5e3)})(n))})),v=(()=>{let e=!1;return n=>{if(e)return;const r=window[a];if(r&&"function"==typeof r.r){const n=r.r({s:Date.now(),r:0,i:t});return g=()=>y(n),h=()=>y(n),s.api.ready(n),s.load.ready(),void(e=!0)}w(n)}})(),C=e=>m().then(h).then((t=>t.runChallenge(e)));return{initChallenges:e=>(e=>((e=>{const t=document.createElement("link");t.rel="preload",t.as="script",t.href=e,document.head.appendChild(t)})(e.url+"/challenges/v2/"+e.siteKey),v(e),(e=>{g().then((t=>t.initChallenges(e))).then((()=>{m=()=>y(void 0),s.init.ready()}))})(e),m().then((()=>C))))(P({internal:e.internal||n},e)),runChallenge:C,runCaptcha:e=>{const t=g().then((({runCaptcha:t})=>t(e)));return{dispose:()=>{t.then((e=>e.dispose()))},promise:t.then((e=>e.promise))}},setUserId:e=>{g().then((t=>t.setUserId(e)))},initPhishing:e=>(v(e),h().then((t=>t.initPhishing(P({internal:e.internal||n},e)))))}},j="https://verify.botbye.com",O=e=>{const{initChallenges:t,runChallenge:n,setUserId:r,initPhishing:i}=C(e||j);return{initChallenges:n=>t(Object.assign({url:n.api||e||j,siteKey:n.clientKey,ver:"1.2.2",internal:{integration:{version:"1.3.0",type:"JS_TAG"}}},n)),runChallenge:n,setUserId:r,initPhishing:t=>i(Object.assign({url:t.api||e||j,internal:{integration:{version:"1.3.0",type:"JS_TAG"}}},t))}},{initChallenges:E,runChallenge:_,setUserId:S,initPhishing:T}=O(j);window.botbye=t})();
</script>

Configuration

Init BotBye on your script with your project client-key:

1
2
3
4
window.botbye.initPhishing({
  // Use your Phishing Project client-key
  clientKey: "00000000-0000-0000-0000-000000000000"
});

Catchers (Optional)

To inject catchers onto your site, await the API and then call the getCatcher method.

1
2
3
4
5
6
const api = await window.botbye.initPhishing({
  // Use your Phishing Project client-key
  clientKey: "00000000-0000-0000-0000-000000000000"
});

const catcherElement = await api.getCatcher();

Insert catcherElement into the site's DOM.

1
2
3
const someForm = document.querySelector('#form');

someForm.append(catcherElement);

getCatcher options

type

Which of the two catchers to build, suited to the different ways attackers host a cloned page:

  • OBJECT (default). An embedded-resource catcher that carries its own detection logic. It keeps working even when the attacker copies every asset of your page and re-serves it from their own infrastructure — it still initiates the callback that reveals the hosting domain.
  • PNG. An invisible image loaded from your origin. It is the lightest option, but it only fires while the cloned page still references the image from your origin — that is, when the attacker leaves resource links pointing back at your site.

We recommend using both catcher types and placing them in different parts of the page. Each type fires in a different cloning scenario, so together they cover both attackers who keep references to your origin and those who copy every resource onto their own infrastructure. Spreading catchers across the page also raises the chance that at least one is carried over when an attacker copies only a part of your HTML.

1
2
3
4
5
// SVG catcher (default)
const svgCatcher = await api.getCatcher({ type: "OBJECT" });

// PNG catcher
const pngCatcher = await api.getCatcher({ type: "PNG" });

skipExecution

By default, a catcher also carries a small script alongside the detection element. When BotBye! confirms a phishing environment, this script lets it deliver an active response — for example, warning the visitor that they are on a fraudulent page and should leave it. Pass skipExecution: true to insert only the detection element, without that script:

1
2
3
4
const catcherElement = await api.getCatcher({
  type: "PNG",
  skipExecution: true
});

url

If you are using Server-Side Integration to hide the BotBye! domain, url points the catcher at your own proxying endpoint instead of the BotBye domain.

The url must match the type: with type: "OBJECT" point it at your SVG catcher endpoint, and with type: "PNG" at your PNG catcher endpoint. The Server-Side Integration guide describes how to expose those endpoints.

1
2
3
4
5
// SVG catcher served from your own domain
const catcherElement = await api.getCatcher({
  type: "OBJECT",
  url: "https://mydomain.com/svg-catcher"
});
1
2
3
4
5
// PNG catcher served from your own domain
const catcherElement = await api.getCatcher({
  type: "PNG",
  url: "https://mydomain.com/png-catcher"
});