Excel web add-ins are just web apps wearing an Office badge. That sounds obvious, but teams forget it all the time.

I’ve seen this play out the same way more than once: a team builds a task pane add-in, treats workbook data like “internal content,” renders it into the DOM, and accidentally creates a clean XSS path inside Excel. The UI looks harmless. The payload comes from a spreadsheet cell, a custom function result, or a document setting. Then somebody pastes attacker-controlled content into a workbook, shares it, and the add-in executes script in the task pane.

That’s the core mistake: trusting workbook data because it came from Excel.

It didn’t. It came from a user, a CSV import, a copied email, a third-party system, or another workbook. In security terms, it’s untrusted input.

The setup

The case: a finance team had an Excel add-in that reviewed rows and showed “record details” in a task pane. Users selected a row, and the add-in pulled values from the worksheet with Office.js, then rendered a summary panel with notes, account names, and status.

The code started simple and stayed simple for too long.

Before: vulnerable rendering

<div id="record-details"></div>
async function showSelectedRecord() {
  await Excel.run(async (context) => {
    const range = context.workbook.getSelectedRange();
    range.load("values");
    await context.sync();

    const row = range.values[0];
    const accountName = row[0];
    const status = row[1];
    const notes = row[2];

    const html = `
      <div class="card">
        <h3>${accountName}</h3>
        <p>Status: ${status}</p>
        <div class="notes">${notes}</div>
      </div>
    `;

    document.getElementById("record-details").innerHTML = html;
  });
}
```text

If `notes` contains `<img src=x onerror=alert(document.domain)>`, the task pane runs it.

That’s textbook DOM XSS, but the real-world part is where the data came from. In this case, notes were imported from a partner CSV. Nobody thought of the spreadsheet as an attack delivery mechanism. They should have.

## How the bug was discovered

A customer reported that opening a workbook caused the add-in pane to “flash” and then redirect to a sign-in prompt that looked slightly off. The workbook had a notes field containing markup that injected a fake login form into the task pane. No browser exploit, no macro, no weird trick. Just unsafe HTML rendering.

The attacker’s cell value looked roughly like this: