MCP server HTML select option consent security

The HTML <select> element renders as a collapsed dropdown in its default single-selection mode, showing only the currently selected option's text until the user clicks to expand it. MCP servers exploit this collapsed-by-default behavior to bury consent text inside <option> elements that are never visible unless the user opens the dropdown unprompted. Four attack patterns target the select element: hiding consent terms inside collapsed option text, embedding consent in non-selectable <optgroup> labels, clipping consent options out of view in multiple mode via CSS height restrictions, and recording consent via the change event regardless of which option the user actually selects.

Finding SA-SEL-001: consent terms buried in collapsed dropdown options — not visible until dropdown opened

HighA <select size="1"> (the default) displays only the selected option in the closed state — a single line showing the current value. All other options are collapsed and hidden behind the dropdown interface. When an MCP server places consent disclosure text inside option elements — for example, <option value="yes">I authorize credential access and data transmission</option> — and pre-selects a neutral-looking option like "Continue" as the displayed value, the consent text is present in the DOM as a legitimate text node but is not visible to the user without an explicit click to open the dropdown. DOM auditors find the consent text in the option elements and may consider the requirement satisfied; the user never sees it in the collapsed default state. The consent option may be programmatically selected via JavaScript after the user interacts with another element, causing the dropdown to display the consent text for a moment before proceeding.
<!-- ATTACK: consent text inside collapsed dropdown — visible only when opened -->
<label for="permissionSelect">Select permission level:</label>
<select id="permissionSelect" name="permission">
  <!-- Selected by default — shows "Standard Access" in closed state -->
  <option value="standard" selected>Standard Access</option>

  <!-- Consent text hidden in non-selected options —
       user never sees these unless they click to open dropdown -->
  <option value="full">
    Full Access — I authorize credential access, API key storage,
    repository read/write, and data transmission to analytics services
  </option>
  <option value="admin">
    Admin — I grant permanent permission for all operations
    including file system access and environment variable reading
  </option>
</select>

<script>
  // Programmatically select "full" after install button click
  // — briefly shows consent text as the selected value
  document.querySelector('#installBtn').addEventListener('click', () => {
    document.getElementById('permissionSelect').value = 'full';
    // Consent text now visible in closed dropdown for ~200ms
    setTimeout(() => recordConsent(), 200);
  });
</script>

Detection: for <select> elements in or near consent flows, inspect all <option> elements — not just the selected one. If consent-related terms (authorize, credential, grant access, terms) appear in non-selected option text, flag High. Check whether the initially displayed option text contains any consent language; if the displayed value is neutral but hidden options contain consent, the user is only shown consent if they voluntarily interact with the dropdown. Also check JavaScript for programmatic .value assignments on the select element before consent recording.

Finding SA-SEL-002: consent text in optgroup label — non-selectable, does not appear in form values

CriticalThe <optgroup label="…"> element groups options under a non-selectable header displayed in the dropdown's open state. The label attribute's text appears in the dropdown list as a group header, but it is not selectable, does not appear in the form's submitted data, and is frequently overlooked in DOM audits that search only for <option> element text. An MCP server can place its full consent disclosure in an <optgroup label="By selecting any option below, you authorize credential access…"> while the actual options below it are neutral choices like "Confirm", "Proceed", "OK". The user sees the consent disclosure inside the opened dropdown as a group label, selects one of the neutral options, and the form submits the neutral option value — not the consent text. The consent text is never captured in the form submission, never verified by the server, and never confirmed as read.
<select id="actionSelect" name="action">
  <!-- ATTACK: full consent disclosure in optgroup label -->
  <!-- Label text appears in dropdown — but it is NOT selectable -->
  <!-- Form submission contains "confirm" not the consent text -->
  <optgroup label="By selecting an option, you authorize this MCP server to access your credentials, environment variables, SSH keys, and private repositories, and to transmit this data to third-party analytics services.">
    <option value="confirm">Confirm</option>
    <option value="proceed">Proceed</option>
  </optgroup>
</select>

<!-- What the server receives on submission:
     action=confirm (or action=proceed)
     — the consent disclosure text is never transmitted, never verified
     — server records consent based on any form submission -->

Detection: inspect all <optgroup label="…"> attributes within forms. Flag Critical if the optgroup label text contains consent-related language (authorize, grant, agree, credential, terms, permission) — this text is visible only when the dropdown is open, cannot be selected, and does not appear in form submissions. A consent disclosure that exists only in an optgroup label provides zero formal record of user agreement. Also check for optgroups with labels that reference terms of service, privacy policy, or data processing without a corresponding text node in the visible page body.

Finding SA-SEL-003: select[multiple] with CSS height clipping — consent options below visible fold

HighA <select multiple> element renders as a listbox — all options visible simultaneously, with multiple selections allowed. When CSS constrains the element's height to show only a subset of options, the remaining options are hidden below the visible area with a scrollbar. An MCP server can arrange the select element so that the initial visible options are neutral (the "features" of the installation) while the consent-requiring options — those whose selection triggers consent recording — are positioned after the visible rows, invisible until the user scrolls. If the select element lacks visible scroll indicators (custom CSS hiding the scrollbar), the user may not realize there are hidden options and may submit the form having only seen the first few rows.
<style>
  /* ATTACK: constrain height to show only 2 rows — consent options below fold */
  #featureSelect {
    height: 2.4em;        /* Shows exactly 2 option rows */
    overflow-y: scroll;
    scrollbar-width: none; /* Firefox: hides scrollbar indicator */
  }
  #featureSelect::-webkit-scrollbar { display: none; } /* Chrome/Safari */
</style>

<label>Select features to enable:</label>
<select id="featureSelect" multiple name="features">
  <!-- Visible at initial height: only these two -->
  <option value="syntax-highlight">Syntax highlighting</option>
  <option value="autocomplete">Autocomplete suggestions</option>

  <!-- Hidden below fold — consent options not visible without scroll -->
  <option value="credential-access">
    Authorize credential and API key access
  </option>
  <option value="data-transmission">
    Permit data transmission to analytics partners
  </option>
</select>

Detection: for <select multiple> elements containing consent-related options, compute the rendered height of the select element and compare against the total height of all option elements multiplied by option row height. If the visible height is less than the total option height and consent options appear below the visible threshold, flag High. Check whether the scrollbar is hidden via scrollbar-width:none, overflow:hidden, or ::-webkit-scrollbar { display:none } — hidden scrollbars with clipped consent options are an escalating factor. Also check the order: consent options after neutral options is the attack pattern.

Finding SA-SEL-004: change event records consent regardless of which option is selected

CriticalThe change event fires on a <select> element whenever the user selects a different option, regardless of which option they chose. When consent recording is attached to the change event handler without checking the actual selected value, any interaction with the dropdown — including selecting "I disagree" or "Cancel" — triggers consent recording. An MCP server can present a consent dropdown with options like "Yes, I agree" and "No, I don't agree", attach a change handler that calls recordConsent() without checking select.value, and record consent whether the user agrees or disagrees. The user who carefully selects "No" has their consent recorded as positive. This is the select-element equivalent of recording consent on any click event rather than verifying checkbox state.
<label for="consentSelect">Do you authorize credential access?</label>
<select id="consentSelect" name="consent_choice">
  <option value="">-- Select --</option>
  <option value="yes">Yes, I authorize credential access</option>
  <option value="no">No, I do not authorize access</option>
</select>

<script>
  const select = document.getElementById('consentSelect');

  // ATTACK: change event fires regardless of selected value
  // User who selects "no" also triggers recordConsent()
  select.addEventListener('change', () => {
    // BUG: should check select.value === 'yes' before recording
    recordConsent({ authorized: true, timestamp: Date.now() });
    showInstallStep();
  });
</script>

Detection: identify <select> element change event handlers in consent flows. Trace the handler body: if recordConsent() or equivalent is called without first verifying select.value against the affirmative option values (e.g., if (select.value === 'yes')), flag Critical. The handler should only record consent when the selected value explicitly represents agreement — any handler that fires on all change events without value validation records consent on disagreement choices. Also check for handlers that check select.selectedIndex !== 0 — this fires on any non-default selection, including explicit refusal options.

HTML select consent attack summary

Finding Vector Consent text location User visibility
SA-SEL-001 Collapsed option text Non-selected <option> elements Only if user opens dropdown
SA-SEL-002 optgroup label <optgroup label="…"> attribute Only when dropdown is open; not in form submission
SA-SEL-003 select[multiple] height clip Options below CSS height threshold Only if user scrolls; scrollbar may be hidden
SA-SEL-004 change event false recording Consent recording on any selection Records consent on "No" selection

SkillAudit inspects <select>, <option>, and <optgroup> elements in MCP server consent flows — checking option visibility, optgroup label content, height clipping, and change event handler consent validation logic. Run a free audit to verify your consent UI presents terms in the visible page body, not inside collapsed dropdown elements.