08 Feb

Remote Port Forwarding using SSH Tunnel

If you want someone on the internet to access a service/port on your local system(and you have access to a remote server), you can use SSH Tunnel to do this.

Step 1: Configure the Remote Server

Edit the /etc/ssh/sshd_config file – you’ll need Root privileges for this. Make sure these values are set…

AllowTcpForwarding no
GatewayPorts yes

Once this is done, restart the SSH Server on the remote server using sudo service ssh restart

Step 2: Run this command on the system that should be accessed…



ssh -nNT -R 8080:localhost:80 binnyva@binnyva.com

Now, just point the browser to http://binnya.com:8080 and the browser will connect to localhost:80. You can change port numbers to access other services.

Source: A visual guide to SSH tunnels

04 Feb

HTML Native Autocomplete

HTML Native Autocomplete – no JavaScript necessary…

<input type="email" size="40" list="defaultEmails">

<datalist id="defaultEmails">
  <option value="jbond007@mi6.defence.gov.uk">
  <option value="jbourne@unknown.net">
  <option value="nfury@shield.org">
  <option value="tony@starkindustries.com">
  <option value="hulk@grrrrrrrr.arg">


05 Jan

Intro summary and details tags in HTML5

The <details> and the <summary> is the easiest way to create an accordean.

Show Spoiler

The Zebra did it!


    <summary>Show Spoiler</summary>
    <p>The Zebra did it!</p>

You can get it to work in line as well…

<style type="text/css">
.inline {
.inline summary {
    text-decoration: underline;
    text-decoration-style: dotted;
.inline span:before {
.inline span:after {

<div>I want to learn
<details class="inline">
    <span>Hyper Text Markup Language</span>
08 Oct

Puppeteer and ShadowDOM

If you are testing an app – be it React, Angular or Vue – and you are using an library that uses shadow DOM to create elements(say, Ionic), you’ll find that there are elements(within the shadow dom) that can’t be accessed using ‘waitForSelector‘ call. If you try making an waitFor or waitForSelector call, it will be a rejected promise.

I ran into this issue while testing an error message that used ion-toast – that component used shadow dom to display the message. I’m sure Ionic uses it in other things like popups and dialogs. So to test that you must be able to peirce the shadow DOM.

There are already suggestions in Puppeteer to create calls like shadow$ or shadow$$. But till that becomes live, you can fix this issue using the query-selector-shadow-dom library.

This library workes with other tools like…

  • WebdriverIO
  • Playwright
  • Puppeteer

Here is an example of it in action…

const puppeteer = require('puppeteer');
const {  QueryHandler } = require("query-selector-shadow-dom/plugins/puppeteer");
(async () => {
    try {
        await puppeteer.__experimental_registerCustomQueryHandler('shadow', QueryHandler);
        const browser = await puppeteer.launch({
            headless: false,
            devtools: true
        const page = await browser.newPage()
        await page.goto('')

        // ensure btn exists and return it
        await page.waitForSelector("shadow/.btn-in-shadow-dom");
        const btn = await page.("shadow/.btn-in-shadow-dom");
        await btn.click();

        // check btn was clicked (this page expected btn to change text of output)
        const outputSpan = await page.("shadow/.output");
        const text = await page.evaluate((output) => output.innerText, outputSpan);
        // prints the text from the output

        await browser.close()
    } catch (e) {


Here are a few examples of how you can use query-selector-shadow-dom with Puppeteer

24 Nov

Database Data to Download Variables

Gets data from a MySql database, splits the data into separate columns, then uses one column in a URL to download something – and uses other column to name the file thats downloaded

for i in (mysql -u root Project_Madapp -sNe 'SELECT CONCAT(id,",",name) AS yo FROM City WHERE type="actual"'); do parts=({i//,/ }); wget -O "{parts[1]}.csv" "http://localhost/student_allocation_csv.php?format=csv&city_id={parts[0]}"; done