WorkshopHelper==============
We're given the URL to a webpage. Once there, we're asked to help outat the workshop. The page gives us a clue about which problem tosolve and then expects us to solve. We're only given ten seconds todiscourage us from being able to do them by hand.
So let's take a quick glance at the source to see what we're dealingwith here... Surprise! All the important stuff is generated somehowat runtime using Javascript.
So that pretty much rules out using Python or a similar scriptinglanguage to fetch and parse the HTML and submit a solution. No sensewading through all that minimized Javascript to figure out what'sgoing on.
Instead we'll use[Puppeteer](https://github.com/GoogleChrome/puppeteer), a Node APIfor headless Chrome. Using the API, we can use Javascript to controland automate a Chrome instance. The API is pretty flexible: we canexplore the DOM of a webpage, simulate user action, save screenshotsof pages, and more.
We can break the task up into several parts:
1. Launch headless Chrome, navigate to the page, and press Start onthe welcome page. We give the page 1 second to respond and load thefirst puzzle page. The code below will open up a visible window andwait 20 milliseconds between each action so that you can watch what'shappening. You can also just let it run at fullspeed withoutdisplaying anything by removing the `headless` and `slowMo`arguments. Puppeteer has a `$` method similar to jQuery's `$` methodor the more modern `document.querySelector` that allows us to selectan element on the page. Once we have an handle to the element,Puppeteer provides a `click` method to simulate user input. Note thatthis code all executes in the Node environment and not the Chromeinstance's web environment. Calling `click` on the div handle isinvoking Puppeteer code to actually simulate user input. It lookssimilar to calling `click` on an actual DOM element but is subtlydifferent.
   ```    const browser = await puppeteer.launch({        args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu'],        headless: false,        slowMo: 20,    });    const page = await browser.newPage();    await page.setViewport({width: 800, height: 600});    await page.goto('http://workshop.web1.sunshinectf.org/');
    const start_button = await page.$('button');
    await start_button.click();    sleep.sleep(1);    ```
1. Parse the header at the top to determine which div has the puzzleto answer. Once we have a handle to the div we can get a handle to its`innerText` property then get its `jsonValue` to transfer the data from the browser to our running Node script. The header is always ofthe form "with a(n) {id|name|class} of {nnnnn}" so we can use a regexto pick out these two pieces.
   ```   const question_h = await page.$('h1#question');   const innerText_handle = await question_h.getProperty('innerText');   const innerText = await innerText_handle.jsonValue();
   const matches = innerText.match('Answer the prompt of the gear with a\\(n\\) ([a-z]+) of ([0-9]+)');
   const prop = matches[1];   const value = matches[2];   ```
1. Select the specified div and extract the equation. This is similarto the previous step except we build a selector on the fly based onwhat was in the header: `div[prop="value"]`. We once again extractthe contents and this time we just `eval` and convert to an integer:
   ```   const selector = '[' + prop + '="' + value + '"]';   const div = await page.$(selector);
   const equation_h3 = await div.$('h3');   const equation_innerText_handle = await equation_h3.getProperty('innerText');   const equation = await equation_innerText_handle.jsonValue();
   const answer = Math.floor(eval(equation)).toString();   ```
1. Supply the answer. Either the form will be a set of radio buttonsor a text box. If it's radio buttons, we find the one whose text isthe same value as the answer and tell Puppeteer to `tap` it.Otherwise if it's a text box, we tell Puppeteer to `type` the answerin. The `delay` parameter just tells Puppeteer to wait 5 millisecondsbetween each keystroke in order to make it more human-like.
   ```   const radio = await div.$('input[type=radio][text="' + answer + '"]');
   if (radio) {       await radio.tap();   } else {       const text = await div.$("input[type=text]");       await text.type(answer, {delay: 5});   }   ```
1. Submit by clicking the button, then repeat ad nauseum. Eventuallyyou are taken to a page with the flag.
   ```   const button = await div.$('button');   await button.tap();   ```