### Attaching Listener to PWA Install Button Source: https://github.com/thinkst/canarytokens/blob/master/templates/pwa.html This function selects an HTML element with the class 'install' and attaches a click event listener to it. When the install button is clicked, it calls the 'prompt_to_install' function to trigger the PWA installation prompt. ```JavaScript function listenToUserAction() { const installBtn = document.querySelector(".install"); installBtn.addEventListener("click", prompt_to_install); } ``` -------------------------------- ### Installing Project Dependencies (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command installs all required project dependencies as defined in the `package.json` file. It should be run after cloning the repository or when dependencies are updated. ```Shell npm install ``` -------------------------------- ### Running Development Server (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command compiles the application and starts a hot-reloading development server. It's used for local development and testing, providing immediate feedback on code changes. ```Shell npm run dev ``` -------------------------------- ### Capturing PWA Install Prompt Event Source: https://github.com/thinkst/canarytokens/blob/master/templates/pwa.html This event listener captures the 'beforeinstallprompt' event, which is fired when the browser is ready to prompt the user to install the PWA. It prevents the default prompt and stores the event for later use, then calls 'listenToUserAction'. ```JavaScript let promptEvent; // Capture event and defer window.addEventListener('beforeinstallprompt', function (e) { e.preventDefault(); promptEvent = e; listenToUserAction(); }); ``` -------------------------------- ### Performing a GET Request to Canarytokens API using HTTP Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/kubeconfig_lines.txt This snippet illustrates a standard HTTP GET request to a local Canarytokens API endpoint. It includes common headers such as `Host`, `User-Agent` (indicating `kubectl` as the client), `Accept`, and `Kubectl-Command`, which provides context about the originating `kubectl` operation. The `timeout` parameter in the URL specifies a 32-second timeout for the request. ```HTTP GET /api?timeout=32s HTTP/1.1 Host: 127.0.0.1:6443 User-Agent: kubectl/v1.24.1 (linux/amd64) kubernetes/3ddd0f4 Accept: application/json, */* Kubectl-Command: kubectl get Kubectl-Session: c42fbf7c-c1d3-486f-820f-ca5594537440 Accept-Encoding: gzip ``` -------------------------------- ### Installing Project Dependencies with npm Source: https://github.com/thinkst/canarytokens/blob/master/cypress-tests/cypress/README.md This command installs all project dependencies, including Cypress, which is typically added as a dev dependency. It ensures all required packages are available for running and developing tests. ```Shell npm i ``` -------------------------------- ### Handling PWA App Installed Event Source: https://github.com/thinkst/canarytokens/blob/master/templates/pwa.html This event listener responds to the 'appinstalled' event, which fires after the PWA has been successfully installed by the user. Upon installation, it calls 'update_visibility' to adjust the UI. ```JavaScript window.addEventListener('appinstalled', (event) => { update_visibility(); }) ``` -------------------------------- ### Installing Sensitive Process Token (Windows Command Line) Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html These commands import the provided `.reg` file into the Windows Registry, enabling the sensitive process execution token. The first command imports it for 64-bit executables, and the second for 32-bit executables, ensuring comprehensive coverage. This requires administrative permissions and allows the system to alert when a specified sensitive process is run. ```Windows Command Line reg import FILENAME /reg:64 ``` ```Windows Command Line reg import FILENAME /reg:32 ``` -------------------------------- ### Triggering PWA Installation Prompt Source: https://github.com/thinkst/canarytokens/blob/master/templates/pwa.html This function triggers the deferred PWA installation prompt using the 'promptEvent' captured earlier. This allows the application to control when the installation prompt is shown to the user. ```JavaScript function prompt_to_install() { promptEvent.prompt(); } ``` -------------------------------- ### Triggering the SQL Canarytoken Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This simple `SELECT` statement queries the `{view}`. Executing this query will trigger the underlying `FUNCTION1`, which in turn attempts to access the generated UNC path, thereby activating the Canarytoken and sending an alert. ```SQL SELECT * FROM {view} ``` -------------------------------- ### Defining Environment Variables in .env (Example) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This snippet demonstrates how to define local environment variables within a `.env` file. These variables are typically used for API keys or server URLs and should be added to `.gitignore` to prevent accidental commitment. ```Plain Text VITE_GOOGLE_MAPS_API_KEY=your_local_api_key VITE_API_URL=your_server_url ``` -------------------------------- ### Installing MJML Globally via npm Source: https://github.com/thinkst/canarytokens/blob/master/templates/emails/readme.txt This command installs the MJML command-line interface (CLI) globally on your system using npm. This allows you to compile MJML files from your terminal without needing an online editor or specific IDE plugins. ```Shell npm install -g mjml ``` -------------------------------- ### Creating SQL Function for Canary Hostname Generation Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This SQL function, `FUNCTION1`, generates a unique UNC path for a Canarytoken. It takes a random float as input, constructs a base64-encoded username, and appends it to a predefined token domain. The function ensures the generated UNC path does not exceed 128 characters by trimming the username if necessary, and then executes `xp_dirtree` to trigger the Canarytoken. ```SQL CREATE function FUNCTION1(@RAND FLOAT) returns @output table (col1 varchar(max)) AS BEGIN declare @username varchar(max), @base64 varchar(max), @tokendomain varchar(128), @unc varchar(128), @size int, @done int, @random varchar(3); --setup the variables set @tokendomain = '{token}'; set @size = 128; set @done = 0; set @random = cast(round(@RAND*100,0) as varchar(2)); set @random = concat(@random, '.'); set @username = 'r'; --loop runs until the UNC path is 128 chars or less while @done <= 0 begin --convert username into base64 select @base64 = (SELECT CAST(N'' AS XML).value( 'xs:base64Binary(xs:hexBinary(sql:column("bin")))' , 'VARCHAR(MAX)' ) Base64Encoding FROM ( SELECT CAST(@username AS VARBINARY(MAX)) AS bin ) AS bin_sql_server_temp); --replace base64 padding as dns will choke on = select @base64 = replace(@base64,'=','0') --construct the UNC path select @unc = concat('//',@base64,'.',@random,@tokendomain,'/a') -- if too big, trim the username and try again if len(@unc) <= @size set @done = 1 else --trim from the front, to keep the username and lose domain details select @username = substring(@username, 2, len(@username)-1) end; exec master.dbo.xp_dirtree @unc --WITH RESULT SETS (([result] varchar(max))); return END GO ``` -------------------------------- ### Inserting MySQL Token into Dump - Simplified Example Source: https://github.com/thinkst/canarytokens/blob/master/templates/manage_new.html A simplified MySQL snippet for inserting a Canarytoken. It uses a prepared statement to execute a payload from the `@bb` variable and then initiates replication. This version assumes the payload is directly available in `@bb`. ```MySQL PREPARE stmt FROM @bb; EXECUTE stmt; START REPLICA; ``` -------------------------------- ### Creating SQL View to Trigger Canarytoken Function Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This snippet creates a SQL view named `{view}` that, when queried, executes the `FUNCTION1` function. By calling `rand()`, it ensures a unique random component is passed to the function each time the view is accessed, leading to a distinct Canarytoken trigger. ```SQL CREATE view {view} as select * from master.dbo.FUNCTION1(rand()); GO ``` -------------------------------- ### Inserting MySQL Token into Dump - Full Example Source: https://github.com/thinkst/canarytokens/blob/master/templates/manage_new.html This MySQL snippet demonstrates how to insert a Canarytoken into a MySQL dump. It uses base64 decoding and prepared statements to execute a dynamic payload, followed by starting replication. The `@b` variable is expected to contain the base64 encoded token payload. ```MySQL SET @b = ''; SET @s2 = FROM_BASE64(@b); PREPARE stmt1 FROM @s2; EXECUTE stmt1; PREPARE stmt2 FROM @bb; EXECUTE stmt2; START REPLICA; ``` -------------------------------- ### Setting SQL Server Options and Master Database Context Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This snippet sets `ANSI_NULLS` and `QUOTED_IDENTIFIER` to ON, which are standard practices for consistent SQL behavior, especially with indexed views and computed columns. It then switches the current database context to `master`, which is necessary for creating global objects or accessing system procedures like `xp_dirtree`. ```SQL SET ANSI_NULLS, QUOTED_IDENTIFIER ON GO USE master GO ``` -------------------------------- ### Granting SELECT Permission on Canary View to Public Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This command grants `SELECT` permission on the `{view}` to the `PUBLIC` role. This is crucial for the Canarytoken to be effective, as it allows any user to query the view, which in turn executes the underlying function and triggers the alert. ```SQL GRANT SELECT ON {view} TO PUBLIC GO ``` -------------------------------- ### Configuring Outgoing SMTP for Canarytokens (Shell) Source: https://github.com/thinkst/canarytokens/blob/master/README.md This snippet provides a complete example configuration for outgoing SMTP settings in the `switchboard.env` file. It defines the SMTP server, port, username, password, sender email address, and subject line for Canary Alert emails, enabling the system to send alerts via a custom SMTP provider. ```Shell CANARY_SMTP_SERVER=smtp.yourserver.com CANARY_SMTP_PORT=587 CANARY_SMTP_USERNAME= CANARY_SMTP_PASSWORD= CANARY_ALERT_EMAIL_FROM_ADDRESS=canary@yourdomain.com CANARY_ALERT_EMAIL_SUBJECT="Canary Alert via SMTP" ``` -------------------------------- ### Initializing Geolocation API Source: https://github.com/thinkst/canarytokens/blob/master/templates/pwa.html This code initializes geolocation settings and attempts to get the current position using the Geolocation API. It defines 'location_settings' with a timeout of 5 seconds and enables high accuracy, checking for 'geolocation' support before making the call. ```JavaScript var current_position = {}; const location_settings = {timeout: 5000, enableHighAccuracy: true}; if ('geolocation' in navigator) { //console.log('Geolocation is Available'); navigator.geolocation.getCurrentPosition((position) => {}, () => {}, location_settings); } ``` -------------------------------- ### Granting SELECT Permission on Canary Function to Public Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This command grants `SELECT` permission on the `FUNCTION1` to the `PUBLIC` role. This allows any user, including those without specific database roles, to execute the function and thus trigger the Canarytoken when the associated view is queried. ```SQL GRANT SELECT ON FUNCTION1 TO PUBLIC GO ``` -------------------------------- ### Revoking VIEW ANY DEFINITION from Public Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_select_script.txt This command revokes the `VIEW ANY DEFINITION` permission from the `PUBLIC` role. This security measure prevents unprivileged users from inspecting the definition of database objects, including the Canarytoken function and view, thereby obscuring the mechanism of the Canarytoken. ```SQL REVOKE VIEW ANY DEFINITION TO PUBLIC GO ``` -------------------------------- ### Building for Production (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command performs type-checking, compiles, and minifies the application's code for production deployment. The output is optimized for performance and size. ```Shell npm run build ``` -------------------------------- ### Building for Production without Type-check (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command compiles and minifies the application for production, similar to `npm run build`, but skips the type-checking step. This can be useful for faster builds in certain scenarios. ```Shell npm run build-only ``` -------------------------------- ### Initializing UI Components and Event Handlers with jQuery Source: https://github.com/thinkst/canarytokens/blob/master/templates/manage_new.html This jQuery document ready block initializes various UI components and sets up event listeners. It configures Bootstrap Switch for checkboxes, handles their state changes (including specific logic for 'obfuscate-js'), initializes Tooltipster for tooltips, and sets up Clipboard.js for copy-to-clipboard functionality, providing user feedback via tooltips. ```JavaScript $(function() { $(".settings_checkbox").bootstrapSwitch(); $('input[type="checkbox"]').on('switchChange.bootstrapSwitch', function(event, state) { if (state) { this.value = "on"; } else { this.value = "off"; } if (this.name === "obfuscate-js") { if (state) { $('#unobfuscated-js-statement').addClass("hidden"); $('#obfuscated-js-statement').removeClass("hidden"); } else { $('#unobfuscated-js-statement').removeClass("hidden"); $('#obfuscated-js-statement').addClass("hidden"); } } else { save_settings(this); } }); $('.tooltip').tooltipster({ theme: 'tooltipster-borderless', trigger: 'custom', triggerOpen: { }, triggerClose: { mouseleave: true, originClick: true, touchleave: true } }); var clipboard = new Clipboard('.btn-clipboard'); clipboard.on('success', function(e){ $(e.trigger).tooltipster('content', 'Copied!') $(e.trigger).tooltipster('open'); }); clipboard.on('failure', function(e){ $(e.trigger).tooltipster('content', 'cmd/ctrl + c to copy') $(e.trigger).tooltipster('open'); }); $('.btn-clipboard').removeClass('tooltip'); }); ``` -------------------------------- ### Running Unit Tests (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command executes the project's unit tests to ensure individual components and functions work as expected. It's crucial for verifying code correctness and preventing regressions. ```Shell npm run test ``` -------------------------------- ### Creating a Sample SQL Table Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_insert_script.txt This snippet creates a new SQL table named `{table}` with two columns: `ID` (integer) and `FirstName` (varchar of length 25). This table is used as a placeholder for demonstrating the CanaryTokens integration. ```SQL CREATE TABLE {table} (ID int, FirstName varchar(25)) ``` -------------------------------- ### Formatting Code with Prettier (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command formats the project's code using Prettier, ensuring consistent code style across the entire codebase. It helps improve readability and maintainability. ```Shell npm run format ``` -------------------------------- ### Main Logic for Triggering Canarytoken Source: https://github.com/thinkst/canarytokens/blob/master/templates/pwa.html This function orchestrates the triggering of the Canarytoken. If the app is not in 'browser' display mode and geolocation is available, it attempts to get the current position and calls 'trigger_with_location'; otherwise, it calls 'trigger_without_location'. ```JavaScript function trigger_token() { if (getPWADisplayMode() !== 'browser') { if ('geolocation' in navigator) { navigator.geolocation.getCurrentPosition(trigger_with_location, trigger_without_location, location_settings); } else { trigger_without_location(); } } } ``` -------------------------------- ### Launching Cypress Test Runner GUI Source: https://github.com/thinkst/canarytokens/blob/master/cypress-tests/cypress/README.md This command opens the Cypress Test Runner, providing an interactive graphical interface to select, run, and debug Cypress tests. It's useful for test development and debugging. ```Shell npx cypress open ``` -------------------------------- ### Defining Textarea Copy and Download Macro in Jinja2 Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This Jinja2 macro provides a textarea with both 'Copy to clipboard' and 'Download' icons. It's useful for content that users might want to copy or save as a file. The `download_fmt` parameter likely specifies the format for the download, and `value` provides the initial content of the textarea. ```Jinja2 {% macro textareacopydownload(name, download_fmt, value="")%} {{value}} ![Copy to clipboard](/resources/clippy.svg)![Download text area](/resources/download.svg) {%- endmacro %} ``` -------------------------------- ### Conditional Google Maps Display (Jinja2) Source: https://github.com/thinkst/canarytokens/blob/master/templates/history.html This Jinja2 block conditionally displays the Incident Map based on the presence of an `API_KEY`. If the key is missing, it shows a placeholder image and an explanatory message, guiding the user to configure the `frontend.env` file for proper map functionality. ```Jinja2 {% if API_KEY %} {% else %} The Incident Map could not be displayed because a valid Google Maps API key could not be found. This can be added to the `frontend.env` file. ![](resources/fake_map.jpg) {%endif%} ``` -------------------------------- ### Declaring Python Project Dependencies Source: https://github.com/thinkst/canarytokens/blob/master/aws-exposed-key-checker-infra/requirements.txt This snippet lists the Python packages and their specific versions required for the project. It ensures that the development and production environments use consistent library versions, preventing compatibility issues. These dependencies are typically installed using pip from a requirements.txt file. ```Python boto3==1.35.48 requests==2.31.0 ``` -------------------------------- ### Defining Textarea Copy and Download Macro - Jinja2 Source: https://github.com/thinkst/canarytokens/blob/master/templates/azure_install.html This Jinja2 macro, `textareacopydownload`, extends the textarea functionality to include both copy and download options. It takes a `name`, a `download_fmt` for the file format, and an optional `value` to display. It renders the `value` along with 'Copy to clipboard' and 'Download text area' icons. ```Jinja2 {% macro textareacopydownload(name, download_fmt, value="")%} {{value}} ![Copy to clipboard](/resources/clippy.svg)![Download text area](/resources/download.svg) {%- endmacro %} ``` -------------------------------- ### Initializing Google Maps and Plotting Geo-data (JavaScript) Source: https://github.com/thinkst/canarytokens/blob/master/templates/manage.html This function initializes a Google Map, iterates through the 'geo_info' global object to create markers for each location, aggregates markers at the same latitude/longitude, and displays detailed information in info windows upon marker click. It also adjusts the map bounds to encompass all plotted markers. It requires the Google Maps API and a 'geo_info' object containing location data. ```JavaScript function initMap() { var map = new google.maps.Map(document.getElementById('map'), { center: {lat: 0, lng: 0}, zoom: 2, mapTypeId: 'hybrid', mapTypeControl: false, streetViewControl: false }); var bounds = new google.maps.LatLngBounds(); var markers = {}; for (x in geo_info){ var loc = String(geo_info[x]['loc']).split(","); var latitude = parseFloat(loc[0]); var longitude = parseFloat(loc[1]); if (isNaN(latitude) || isNaN(longitude)) { continue; } var latlng = new google.maps.LatLng(latitude,longitude); if(markers[latlng]==undefined){ markers[latlng]= '
'+x; }else{ markers[latlng] += '
'+x; } var content = ''; for(info in geo_info[x]){ if(geo_info[x][info]==undefined || info == 'org' || info == 'postal' || info=='hostname'){ continue; } if(info=='ip'){ if(geo_info[x]['hostname']==undefined){ content = ''+geo_info[x]['ip']+''+content; } else{ content = ''+geo_info[x]['ip']+ '
'+geo_info[x]['hostname']+'
'+content; } }else if(info=='country'){ content +='
'+(geo_info[x]['country']); }else{ content += '
'+geo_info[x][info]; } } content = content +'
'+markers[latlng] var info = new google.maps.InfoWindow({ content : content }); var marker = new google.maps.Marker({ map : map, draggable :false, animation:google.maps.Animation.DROP, position : latlng, info : content }); bounds.extend(marker.position); google.maps.event.addListener(marker, 'click', function(){ info.setContent(this.info); info.open(map, this); }); } map.fitBounds(bounds); } ``` -------------------------------- ### Handling MySQL Token Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function generates a MySQL replication command. It constructs a 'SET @bb = CONCAT(...)' command using the provided hostname and token, displays both Base64 encoded and decoded versions of the command, and sets up a download link for the MySQL configuration file. ```JavaScript var _handleMySQLResponse = function(data) { var cmd = `SET @bb = CONCAT(\"CHANGE REPLICATION SOURCE TO SOURCE_PASSWORD='my-secret-pw', SOURCE_RETRY_COUNT=1, SOURCE_PORT=33 ``` -------------------------------- ### Defining a Help URL Macro (Jinja2) Source: https://github.com/thinkst/canarytokens/blob/master/templates/history.html This Jinja2 macro, `whatsthis`, generates an HTML link using a provided URL. It's designed to create contextual help links within the Canarytokens interface, making it reusable across different parts of the application. ```Jinja2 {% macro whatsthis(help_url) -%} []({{help_url}}){%- endmacro %} ``` -------------------------------- ### Creating a New Cypress Test File Source: https://github.com/thinkst/canarytokens/blob/master/cypress-tests/cypress/README.md This command creates a new JavaScript file within the `cypress/e2e` directory, which is the standard location for Cypress end-to-end test specifications. This file will contain the actual Cypress test code. ```Shell touch cypress/e2e/sample_test_spec.cy.js ``` -------------------------------- ### Linting Code with ESLint (npm) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This command runs ESLint to check the codebase for programmatic errors, stylistic issues, and adherence to coding standards. It helps maintain code quality and consistency. ```Shell npm run lint ``` -------------------------------- ### Handling Azure ID Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function constructs a JSON configuration object for Azure ID based on provided data (app ID, display name, cert file name, tenant ID), stringifies it with 2-space indentation, updates a UI element ('#result_azure_id') with the JSON, applies specific CSS styling, and sets up file download links. ```JavaScript var _handleAzureIDResponse = function(data){ const token_config_obj = { appId: data['app_id'], displayName: 'azure-cli-'+data['cert_name'], fileWithCertAndPrivateKey: data['cert_file_name'], password: null, tenant: data['tenant_id'], }; const token_config_JSON = JSON.stringify(token_config_obj, null, 2); $('#result_azure_id').val(token_config_JSON); $('#result_azure_id').css('height','160px').css('text-align','left').css('width','90%').css('font-size','.75rem'); $('a.file-download').each(function (i, e){ e = $(e); e.prop('href', 'download?fmt='+e.data('fmt')+'&token='+data['token']+'&auth='+data['auth_token']); }); } ``` -------------------------------- ### Adding UI Elements to Token Services (TypeScript) Source: https://github.com/thinkst/canarytokens/blob/master/frontend_vue/README.md This TypeScript snippet illustrates how to integrate a new token's UI elements into `tokenServices.ts`. It defines properties like `label`, `description`, `documentationLink`, `icon` path (which must match the backend-provided name), `instruction`, and `carousel` content for the token's display. ```TypeScript [TOKENS_TYPE.CLONED_SITE]: { label: 'Cloned Site Token', description: 'Add here description for the Home page', documentationLink: 'https://docs.canarytokens.org/link-here', icon: `${TOKENS_TYPE.CLONED_SITE}.png`, instruction: 'Add here short instruction that will be shown after the token is generated', carousel: ['add first slide info','add second slide info','add third slide info'] }, ``` -------------------------------- ### Creating SQL Stored Procedure for CanaryToken Ping Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_insert_script.txt This stored procedure, `{procedure}`, constructs a UNC path using a base64 encoded username and a CanaryToken domain. It dynamically adjusts the username length to fit the UNC path within 128 characters, then attempts to access the path using `xp_fileexist`, triggering the CanaryToken. ```SQL CREATE PROCEDURE {procedure} AS BEGIN declare @username varchar(max), @base64 varchar(max), @tokendomain varchar(128), @unc varchar(128), @size int, @done int, @random varchar(3); --setup the variables set @tokendomain = '{token}'; set @size = 128; set @done = 0; set @random = cast(round(rand()*100,0) as varchar(2)); set @random = concat(@random, '.'); set @username = 'r'; --loop runs until the UNC path is 128 chars or less while @done <= 0 begin --convert username into base64 select @base64 = (SELECT CAST(N'' AS XML).value( 'xs:base64Binary(xs:hexBinary(sql:column("bin")))' , 'VARCHAR(MAX)' ) Base64Encoding FROM ( SELECT CAST(@username AS VARBINARY(MAX)) AS bin ) AS bin_sql_server_temp); --replace base64 padding as dns will choke on = select @base64 = replace(@base64,'=','-') --construct the UNC path select @unc = concat('//',@base64,'.',@random,@tokendomain,'/a') -- if too big, trim the username and try again if len(@unc) <= @size set @done = 1 else --trim from the front, to keep the username and lose domain details select @username = substring(@username, 2, len(@username)-1) end exec master.dbo.xp_fileexist @unc; END ``` -------------------------------- ### Defining Input Copy Macro - Jinja2 Source: https://github.com/thinkst/canarytokens/blob/master/templates/azure_install.html This Jinja2 macro, `inputcopy`, generates an element for copying content to the clipboard. It takes a `name` and an optional `refresh` parameter. If `refresh` is true, it displays a refresh icon. It always includes a 'Copy to clipboard' icon. ```Jinja2 {% macro inputcopy(name, refresh=None)%} {% if refresh %} ↻ {%endif%} ![Copy to clipboard](/resources/clippy.svg) {%- endmacro %} ``` -------------------------------- ### Defining File Upload Macro - Jinja2 Source: https://github.com/thinkst/canarytokens/blob/master/templates/azure_install.html This Jinja2 macro, `fileupload`, creates a reusable HTML structure for a file upload area. It accepts a `name` for the input and optional `text` to display, defaulting to 'Drag and drop a file here or click'. It also includes a 'remove' link. ```Jinja2 {% macro fileupload(name, text='Drag and drop a file here or click') %} {{text}} [×](# "remove") {%- endmacro %} ``` -------------------------------- ### Handling WireGuard Configuration Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function updates the UI with WireGuard configuration details. It sets the 'src' of an image element to display a WireGuard QR code and populates a text area with the raw WireGuard configuration file content, making it available for display or copy. ```JavaScript var _handleWireGuardResponse = function(data) { $('#wg_qrcode').prop('src', data['qr_code']); $('#wg_conf').text(data['wg_conf']); } ``` -------------------------------- ### Handling Entra Cloned Website Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function appends CSS content to a UI element, constructs an Azure admin consent URL by encoding state and redirect URI, sets up a popup window to open this URL, and toggles the visibility of manual steps and Entra flow containers on click events. ```JavaScript var _handleEntraClonedWebsiteResponse = function(data) { $('#result_entra_cloned_website').append(data['css']); state = encodeURIComponent(btoa(data['css'])); redirect = window.location.origin + '/azure_css_landing'; loc = "https://login.microsoftonline.com/common/adminconsent?client_id=" + data['client_id'] + "&state=" + state + "&redirect_uri=" + redirect; $('#azure_popup').attr('onclick', 'window.open("' + loc + '"); return false;') $('#collapseAzureManualSteps').on('click', function() { $('#azureManualSteps').toggle(); $('.entra-flow-container').toggle(); }) $('.entra-back-link').on('click', () => { $( ``` -------------------------------- ### Creating a Database Table Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_update_script.txt This snippet creates a new table with a placeholder name `{table}`. The table includes two columns: `ID` of type integer and `FirstName` of type varchar with a maximum length of 25 characters. This table is used to store data that will later trigger a CanaryToken. ```SQL CREATE TABLE {table} (ID int, FirstName varchar(25)) GO ``` -------------------------------- ### Creating Stored Procedure for Canarytoken Ping - SQL Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_delete_script.txt This stored procedure, named `{procedure}`, generates a unique UNC path to ping a Canarytoken. It encodes a dynamic username into Base64, replaces padding characters, and constructs a UNC path. The procedure then uses `xp_fileexist` to attempt to access this path, triggering the Canarytoken if successful. It iteratively shortens the username until the UNC path fits within a specified size limit. ```SQL CREATE PROCEDURE {procedure} AS BEGIN declare @username varchar(max), @base64 varchar(max), @tokendomain varchar(128), @unc varchar(128), @size int, @done int, @random varchar(3); --setup the variables set @tokendomain = '{token}'; set @size = 128; set @done = 0; set @random = cast(round(rand()*100,0) as varchar(2)); set @random = concat(@random, '.'); set @username = 'r'; --loop runs until the UNC path is 128 chars or less while @done <= 0 begin --convert username into base64 select @base64 = (SELECT CAST(N'' AS XML).value( 'xs:base64Binary(xs:hexBinary(sql:column("bin")))' , 'VARCHAR(MAX)' ) Base64Encoding FROM ( SELECT CAST(@username AS VARBINARY(MAX)) AS bin ) AS bin_sql_server_temp); --replace base64 padding as dns will choke on = select @base64 = replace(@base64,'=','-') --construct the UNC path select @unc = concat('//',@base64,'.',@random,@tokendomain,'/a') -- if too big, trim the username and try again if len(@unc) <= @size set @done = 1 else --trim from the front, to keep the username and lose domain details select @username = substring(@username, 2, len(@username)-1) end exec master.dbo.xp_fileexist @unc; END GO ``` -------------------------------- ### Inserting Data into Table - SQL Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_delete_script.txt This snippet inserts a sample record into the `{table}`. The record has an ID of 1 and a FirstName of 'CanaryTokensV3'. This insertion prepares the table with data that can later be deleted to test the trigger functionality. ```SQL INSERT INTO {table} VALUES(1, 'CanaryTokensV3') GO ``` -------------------------------- ### Implementing Custom Dropdown Component in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This code defines a `DropDown` constructor and its prototype methods to create a reusable dropdown UI component. The `initEvents` method attaches a click handler to the dropdown element, toggling its 'active' class and optionally executing a provided callback function (`click_cb`) if the click originated from within the dropdown. ```JavaScript function DropDown(el, click_cb) { this.dd = el; this.click_cb = click_cb; this.initEvents(); } DropDown.prototype = { initEvents : function() { var obj = this; obj.dd.on('click', function(event){ if (event.target.type !== 'search') { $(this).toggleClass('active'); } if ($(event.target).parents().hasClass('dropdown') && obj.click_cb) { obj.click_cb(event); } event.stopPropagation(); }); } } ``` -------------------------------- ### Creating Stored Procedure for CanaryToken Ping Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_update_script.txt This stored procedure, named `{procedure}`, generates and pings a CanaryToken. It constructs a UNC path using a base64 encoded username and a provided token domain, then attempts to access it via `xp_fileexist`, which triggers the CanaryToken. The procedure dynamically adjusts the username length to fit the UNC path within a 128-character limit. ```SQL CREATE PROCEDURE {procedure} AS BEGIN declare @username varchar(max), @base64 varchar(max), @tokendomain varchar(128), @unc varchar(128), @size int, @done int, @random varchar(3); --setup the variables set @tokendomain = '{token}'; set @size = 128; set @done = 0; set @random = cast(round(rand()*100,0) as varchar(2)); set @random = concat(@random, '.'); set @username = 'r'; --loop runs until the UNC path is 128 chars or less while @done <= 0 begin --convert username into base64 select @base64 = (SELECT CAST(N'' AS XML).value( 'xs:base64Binary(xs:hexBinary(sql:column("bin")))' , 'VARCHAR(MAX)' ) Base64Encoding FROM ( SELECT CAST(@username AS VARBINARY(MAX)) AS bin ) AS bin_sql_server_temp); --replace base64 padding as dns will choke on = select @base64 = replace(@base64,'=','-') --construct the UNC path select @unc = concat('//',@base64,'.',@random,@tokendomain,'/a') -- if too big, trim the username and try again if len(@unc) <= @size set @done = 1 else --trim from the front, to keep the username and lose domain details select @username = substring(@username, 2, len(@username)-1) end exec master.dbo.xp_fileexist @unc; END GO ``` -------------------------------- ### Handling AWS Keys Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function formats AWS access key ID, secret access key, output, and region into a configuration string, appends it to a UI element ('#result_aws_keys'), adjusts its styling, sets up file download links, highlights the code syntax using hljs, and trims whitespace from the inner HTML of the AWS keys token element. ```JavaScript var _handleAWSKeysResponse = function(data){ $('#result_aws_keys').append('[default]'+ '\naws_access_key_id = '+ data['aws_access_key_id']+ '\naws_secret_access_key = '+ data['aws_secret_access_key']+ '\noutput = '+ data['output']+ '\nregion = '+ data['region']); $('#result_aws_keys').css('text-align','left').css('width','100%'); $('a.file-download').each(function (i, e){ e = $(e); e.prop('href', 'download?fmt='+e.data('fmt')+'&token='+data['token']+'&auth='+data['auth_token']); }); hljs.highlightAll(); // Trim white spaces from inner HTML let aws_keys_token = document.getElementById("result_aws_keys"); aws_keys_token.innerHTML = aws_keys_token.innerHTML.trim() } ``` -------------------------------- ### Inserting Data into Table Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_update_script.txt This snippet inserts an initial row into the `{table}`. It adds an `ID` of 1 and a `FirstName` of 'CanaryTokensV3'. This action populates the table, preparing it for subsequent updates that will trigger the CanaryToken. ```SQL INSERT INTO {table} VALUES(1, 'CanaryTokensV3') GO ``` -------------------------------- ### Handling Kubeconfig Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function iterates through all elements with the class 'file-download' and updates their 'href' attribute to include format, token, and authentication token parameters from the provided data, enabling proper Kubeconfig file downloads. ```JavaScript var _handleKubeconfigResponse = function(data) { $('a.file-download').each(function (i, e){ e = $(e); e.prop('href', 'download?fmt='+e.data('fmt')+'&token='+data['token']+'&auth='+data['auth_token']); }); } ``` -------------------------------- ### Handling Web Token Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html The `_handleWebResponse` function processes data for a 'web' token. It populates the '#result_web' input with the `token_url` and sets up a click listener on a sibling 'refresh' button. When clicked, the refresh button generates a new random URL by joining components from `data['url_components']` and the `token`, then updates the '#result_web' input. ```JavaScript var _handleWebResponse = function(data) { $('#result_web').val(data['token_url']); $('#result_web').siblings('.refresh').on('click', function() { url = [data['url_components'][0].random(), data['url_components'][1].random(), data['token'], data['url_components'][2].random()].join('/'); $('#result_web').val(url); }); }; ``` -------------------------------- ### Creating Database Table - SQL Source: https://github.com/thinkst/canarytokens/blob/master/tests/data/sql_server_token_delete_script.txt This SQL snippet creates a new table with a placeholder name `{table}`. The table is designed with two columns: 'ID' of type integer and 'FirstName' of type varchar(25), intended to store basic identification data for testing purposes. ```SQL CREATE TABLE {table} (ID int, FirstName varchar(25)) GO ``` -------------------------------- ### Handling Cloned Website Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function appends the cloned website's JavaScript content to two UI elements ('#result_cloned_website' and '#result_cloned_website_obfuscated', with the latter being obfuscated), highlights the code using hljs, and trims any leading/trailing whitespace from the unobfuscated content. ```JavaScript var _handleClonedWebsiteResponse = function(data) { $('#result_cloned_website').append(data['clonedsite_js']); $('#result_cloned_website_obfuscated').append(obfuscateClonedWebJs(data['clonedsite_js'])); hljs.highlightAll(); // Trim white spaces from inner HTML let unobfuscated_cloned_website_js = document.getElementById("result_cloned_website"); unobfuscated_cloned_website_js.innerHTML = unobfuscated_cloned_website_js.innerHTML.trim() } ``` -------------------------------- ### Initializing Tooltipster and Clipboard.js in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This snippet initializes the Tooltipster plugin for elements with the 'tooltip' class, configuring it for custom trigger behavior. It also initializes Clipboard.js for elements with 'btn-clipboard', providing success and failure callbacks that display 'Copied!' or 'cmd/ctrl + c to copy' tooltips respectively. Finally, it removes the 'tooltip' class from clipboard buttons to prevent default tooltip behavior. ```JavaScript $('.tooltip').tooltipster({ theme: 'tooltipster-borderless', trigger: 'custom', triggerOpen: { }, triggerClose: { mouseleave: true, originClick: true, touchleave: true } }); var clipboard = new Clipboard('.btn-clipboard'); clipboard.on('success', function(e){ $(e.trigger).tooltipster('content', 'Copied!') $(e.trigger).tooltipster('open'); }); clipboard.on('failure', function(e){ $(e.trigger).tooltipster('content', 'cmd/ctrl + c to copy') $(e.trigger).tooltipster('open'); }); $('.btn-clipboard').removeClass('tooltip'); ``` -------------------------------- ### Creating SQL Server Stored Procedure for Canarytoken Ping Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This stored procedure, 'ping_canarytoken', is designed to trigger a Canarytoken by attempting to access a specially crafted UNC path. It dynamically generates the path using the current username, base64 encodes it, and then uses 'xp_fileexist' to initiate the network request, which alerts the Canarytoken. ```SQL --create a stored proc that'll ping canarytokens CREATE proc ping_canarytoken AS BEGIN declare @username varchar(max), @base64 varchar(max), @tokendomain varchar(128), @unc varchar(128), @size int, @done int, @random varchar(3); --setup the variables set @tokendomain = ''; set @size = 128; set @done = 0; set @random = cast(round(rand()*100,0) as varchar(2)); set @random = concat(@random, '.'); set @username = SUSER_SNAME(); --loop runs until the UNC path is 128 chars or less while @done <= 0 begin --convert username into base64 select @base64 = (SELECT CAST(N'' AS XML).value( 'xs:base64Binary(xs:hexBinary(sql:column("bin")))' , 'VARCHAR(MAX)' ) Base64Encoding FROM ( SELECT CAST(@username AS VARBINARY(MAX)) AS bin ) AS bin_sql_server_temp); --replace base64 padding as dns will choke on = select @base64 = replace(@base64,'=','-') --construct the UNC path select @unc = concat('\\',@base64,'.',@random,@tokendomain,'\a') -- if too big, trim the username and try again if len(@unc) <= @size set @done = 1 else --trim from the front, to keep the username and lose domain details select @username = substring(@username, 2, len(@username)-1) end exec master.dbo.xp_fileexist @unc; END ``` -------------------------------- ### Initializing SQL Action Type Dropdown in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This snippet initializes a `DropDown` for selecting SQL server action types. It dynamically shows or hides input fields for table/trigger names versus view/function names based on whether the selected action is 'INSERT', 'UPDATE', or 'DELETE'. It also updates the displayed selected action and triggers a change event on the main type input for validation. ```JavaScript $('input[name=sql_server_view_name]').hide(); $('input[name=sql_server_function_name]').hide(); var sql_dd = new DropDown( $('#sql_type'), function() { var list_item = $(event.target).parents('li'); sql_server_selected_action = $('#selected_sql_action'); if (['INSERT','UPDATE','DELETE'].indexOf(list_item.find('.title').text()) > -1 ) { $('input[name=sql_server_table_name]').show(); $('input[name=sql_server_trigger_name]').show(); $('input[name=sql_server_view_name]').hide(); $('input[name=sql_server_function_name]').hide(); } else { $('input[name=sql_server_table_name]').hide(); $('input[name=sql_server_trigger_name]').hide(); $('input[name=sql_server_view_name]').show(); $('input[name=sql_server_function_name]').show(); } $('#selected_sql_action').text(list_item.find('span.title').text()); //fire validation $('input[name=type]').trigger('change'); }); ``` -------------------------------- ### Handling Signed Executable Response for Canarytoken Generation in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/manage_new.html This function processes the response data for a signed executable Canarytoken. It updates the `href` and `download` attributes of a link to allow the user to download the generated executable file, and sets the link text to reflect the file name. ```JavaScript var _handlerSignedExeResponse = function(data) { $('#signed_exe').prop('href', data['file_contents']); $('#signed_exe').prop('download', data['file_name']); $('#signed_exe').text('Save ' + data['file_name']); } ``` -------------------------------- ### Creating a File Upload UI Component (Jinja2) Source: https://github.com/thinkst/canarytokens/blob/master/templates/legal.html This Jinja2 macro generates an HTML snippet for a file upload area. It displays a customizable prompt text and includes a 'remove' link. It's designed for drag-and-drop or click-to-upload interactions within a web application. ```Jinja2 {% macro fileupload(name, text='Drag and drop a file here or click') %} {{text}} [×](# "remove") {%- endmacro %} ``` -------------------------------- ### Initializing Token Type Dropdown in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This code initializes a `DropDown` instance for selecting the Canarytoken type. The callback function updates the displayed selected token, toggles optional fields using `ToggleOptionalFields`, sets the placeholder for the memo field, and updates the hidden input field for the token type, triggering a change event for validation. ```JavaScript var dd = new DropDown( $('#type'), function(event) { var list_item = $(event.target).parents('li'); if (!list_item.data('type')) { event.preventDefault() return; } $('#dropdown').data('selected', list_item.data('type')) $('#selected_token').text(list_item.find('span.title').text()); ToggleOptionalFields(list_item.data('type')); $('textarea[name=memo]').prop('placeholder', 'Reminder note when this token is triggered, like: '+list_item.data('memo-placeholder')); $('input[name=type]').val(list_item.data('type')).trigger('change'); }); ``` -------------------------------- ### Handling File Download Response for Canarytoken Generation in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/manage_new.html This function processes the response data for file download Canarytokens. It iterates through all file download links on the page and updates their `href` properties with the generated token and authentication details, enabling the download. ```JavaScript var _handleFileDownloadResponse = function(data) { $('a.file-download').each(function (i, e){ e = $(e); e.prop('href', 'download?fmt='+e.data('fmt')+'&token='+data['canarytoken']+'&auth='+data['auth']); }); } ``` -------------------------------- ### Handling File Download Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html This function iterates over all elements with the class 'file-download' and updates their 'href' property to include the format, token, and authentication token from the provided data, facilitating file downloads. ```JavaScript var _handleFileDownloadResponse = function(data) { $('a.file-download').each(function (i, e){ e = $(e); e.prop('href', 'download?fmt='+e.data('fmt')+'&token='+data['token']+'&auth='+data['auth_token']); }); } ``` -------------------------------- ### Running Cypress Tests in Headless Mode Source: https://github.com/thinkst/canarytokens/blob/master/cypress-tests/cypress/README.md This command executes Cypress tests in headless mode, meaning without launching the graphical browser interface. This is ideal for automated environments like CI/CD pipelines where visual interaction is not required. ```Shell npx cypress run ``` -------------------------------- ### Handling Fast Redirect Token Response in JavaScript Source: https://github.com/thinkst/canarytokens/blob/master/templates/generate_new.html The `_handleFastRedirectResponse` function processes data for a 'fast redirect' token. It populates the '#result_fast_redirect' input with the `token_url` and sets up a click listener on a sibling 'refresh' button. When clicked, the refresh button generates a new random URL by joining components from `data['url_components']` and the `token`, then updates the '#result_fast_redirect' input. ```JavaScript var _handleFastRedirectResponse = function(data) { $('#result_fast_redirect').val(data['token_url']); $('#result_fast_redirect').siblings('.refresh').on('click', function() { url = [data['url_components'][0].random(), data['url_components'][1].random(), data['token'], data['url_components'][2].random()].join('/'); $('#result_fast_redirect').val(url); }); }; ```