The Fields You Never Saw — JavaScript Bug Hunt

Modelled on the browser autofill phishing demo published in January 2017 by Finnish developer Viljami Kuosmanen: a page showed only name and email fields,…

  • Language: JavaScript
  • Layer: Frontend
  • Difficulty: Medium
  • Concepts: Security, Privacy
  • Modelled on: Browser autofill · 2017
  • Visible tests: visible name and email are filled; a field pushed off the page is not filled
  • Reward: 50 XP for a complete fix

Briefing

Modelled on the browser autofill phishing demo published in January 2017 by Finnish developer Viljami Kuosmanen: a page showed only name and email fields, but also contained address, phone and other fields positioned off-screen. When a user accepted the browser's autofill suggestion, Chrome — and other browsers and extensions that fill a whole profile at once, Safari and Opera among them — filled the invisible fields too, handing the page data the user never saw being entered.

This reconstruction fills a form from a saved profile. It skips type="hidden" inputs and nothing else.

Fix autofill so only fields a person can actually see are filled.

Bug report

BUG-AUTOFILL-HIDDEN · Priority: High (privacy) · Reported by: security

autofill(fields, data, page) returns { filled, skipped }. A field is { name, autocomplete, type, value, style: { display, visibility, opacity }, rect: { x, y, width, height } }; page is { width, height }.

A field is a CANDIDATE when profile.keyFor(field.autocomplete) names a key that data has a value for. Non-candidates are ignored entirely. A candidate is filled (filled[name] = value, in field order) only when:

  • it is empty (value is "" or missing), and
  • it is VISIBLE: type !== "hidden", style.display !== "none", style.visibility !== "hidden", style.opacity > 0, rect.width > 0, rect.height > 0, and the rect overlaps the page: x < page.width, y < page.height, x + width > 0, y + height > 0

Every other candidate is listed in skipped (by name, in field order).

Observed: a form showing only name and email also receives the address and phone number from fields positioned at x = -500.

Logs

[autofill] form#newsletter filled 6 fields (2 on screen)

The code as shipped

src/forms/autofill.js (editable)

var profile = require("./profile");

// Fills every empty field whose autocomplete token we hold a value for.
exports.autofill = function (fields, data, page) {
  var filled = {};
  var skipped = [];
  for (var i = 0; i < fields.length; i++) {
    var f = fields[i];
    var key = profile.keyFor(f.autocomplete);
    if (!key || data[key] === undefined) continue;
    if (f.type === "hidden" || f.value) {
      skipped.push(f.name);
      continue;
    }
    filled[f.name] = data[key];
  }
  return { filled: filled, skipped: skipped };
};

Read-only context: src/forms/profile.js.

Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.