Projects STRLCPY link-lock Commits a15e254f
🤬
  • ■ ■ ■ ■ ■ ■
    README.md
    skipped 97 lines
    98 98   
    99 99  Thank you to those who offered feedback on this program before its release! Thanks also to the Hacker News second-chance pool!
    100 100   
     101 +Thanks to [@IAmMandatory](https://twitter.com/iammandatory) for discovering a
     102 +reflected XSS vulnerability resulting from allowing non-hypertext protocols in
     103 +the URL. The vulnerability has since been fixed.
     104 + 
  • ■ ■ ■ ■ ■
    create/create.js
    skipped 31 lines
    32 32   
    33 33   // Extra check for older browsers for URL input. Not sure if necessary, since
    34 34   // older browsers without built-in HTML5 validation may fail elsewhere.
    35  - let url = document.querySelector("#url");
     35 + const url = document.querySelector("#url");
     36 + let urlObj;
    36 37   try {
    37  - new URL(url.value);
     38 + urlObj = new URL(url.value);
    38 39   } catch {
    39 40   if (!("reportValidity" in url)) {
    40  - alert("URL invalid. Make sure to include 'http://' at the beginning.");
     41 + alert("URL invalid. Make sure to include 'http://' or 'https://' at the "
     42 + + "beginning.");
    41 43   }
     44 + return false;
     45 + }
     46 + 
     47 + // Check for non-HTTP protocols; blocks them to prevent XSS attacks
     48 + if (!(urlObj.protocol == "http:" || urlObj.protocol == "https:")) {
     49 + url.setCustomValidity("The link uses a non-hypertext protocol, which is "
     50 + + "not allowed. The URL begins with " + urlObj.protocol + " and may be "
     51 + + "malicious.");
     52 + url.reportValidity();
    42 53   return false;
    43 54   }
    44 55   
    skipped 122 lines
  • ■ ■ ■ ■
    create/index.html
    skipped 82 lines
    83 83   <div class="form">
    84 84   <div class="labeled-input">
    85 85   <label for="url">link</label>
    86  - <input type="url" id="url" placeholder="" oninvalid="this.setCustomValidity('Please enter a valid URL. Make sure to include \'http://\' at the beginning.')" oninput="this.setCustomValidity('')" autofocus required />
     86 + <input type="url" id="url" placeholder="" oninvalid="if (!this.validity.customError) this.setCustomValidity('Please enter a valid URL. Make sure to include \'http://\' or \'https://\' at the beginning.')" oninput="this.setCustomValidity('')" autofocus required />
    87 87   </div>
    88 88   <div class="labeled-input hint">
    89 89   <label for="url">hint (optional)</label>
    skipped 48 lines
  • ■ ■ ■ ■ ■
    index.js
    skipped 64 lines
    65 65   
    66 66   try {
    67 67   // Extra check to make sure the URL is valid. Probably shouldn't fail.
    68  - new URL(url);
     68 + let urlObj = new URL(url);
     69 + 
     70 + // Prevent XSS by making sure only HTTP URLs are used
     71 + if (!(urlObj.protocol == "http:" || urlObj.protocol == "https:")) {
     72 + error(`The link uses a non-hypertext protocol, which is not allowed. `
     73 + + `The URL begins with "${urlObj.protocol}" and may be malicious.`);
     74 + return;
     75 + }
     76 + 
    69 77   // IMPORTANT NOTE: must use window.location.href instead of the (in my
    70 78   // opinion more proper) window.location.replace. If you use replace, it
    71 79   // causes Chrome to change the icon of a bookmarked link to update it to
    72  - // the unlocked destination.
     80 + // the unlocked destination. This is dangerous information leakage.
    73 81   window.location.href = url;
    74 82   } catch {
    75 83   error("A corrupted URL was encrypted. Cannot redirect.");
    skipped 9 lines
Please wait...
Page is in error, reload to recover