Winnow Tools

Security practices and incident response

Version 1.0 · 31 August 2026 · reviewed at least annually and after any incident.

This document describes how we actually build and release our Atlassian Marketplace apps, and what we do when someone reports a security problem. It is written to be checked, not to reassure: where a control does not exist, it says so.

Winnow Tools is a one-person shop. That fact shapes everything below — some controls are stronger for it, others are absent because of it, and we would rather you knew which is which before you install anything.

1. Design: keeping the blast radius small

The strongest security control we have is not a process, it is the shape of the app. Our apps run entirely on Atlassian Forge, inside Atlassian's own infrastructure. We operate no servers, so there is no backend of ours to breach and no copy of customer data outside Atlassian.

Concretely, for Notify for Google Chat:

Permissions Exactly three: read:jira-work, read:jira-user, storage:app. The app holds no write access to Jira at all — it cannot create, edit or delete anything in your site.
Outbound network One declared host, chat.googleapis.com. Forge blocks every other destination at the platform level. This is enforced by Atlassian, not by our code, so a bug in our code cannot widen it.
Inbound network None. There are no web triggers, no public endpoints and no API of our own. Nothing outside Atlassian can invoke our code.
Data at rest Rules and a short delivery log, in Forge hosted storage scoped to your site. No issue summaries, descriptions or comment text is retained — those pass through and are discarded.
Personal data Only Atlassian account identifiers, never names or email addresses. Display names are read from Jira each time a page is opened and are not stored.

A weekly job removes account identifiers belonging to deleted users from saved rules. If removing them would empty a rule's people filter, the rule is disabled rather than silently widened — quietly turning "notify about these three people" into "notify about everyone" would be a data-exposure bug, not a convenience.

2. Authorisation

Every function that the settings page can call begins by verifying that the caller holds the Jira ADMINISTER permission. This is not cosmetic: Forge resolvers can be invoked outside the page that normally displays them, so a check that lives only in the user interface is not a check at all.

Beyond that, reads made on behalf of a person run as that person (asUser), so Jira itself decides what they may see. App-level access (asApp) is used only where no human is present: handling an incoming event and running the weekly cleanup job.

We shipped this as a correction. An earlier version performed privileged reads without a permission check, which would have let any logged-in user enumerate projects and people and rewrite notification rules. Atlassian's automated review caught it before release, and it was right to.

3. Handling untrusted input

Issue titles, comments and user names are written by people we do not control, and they end up inside a message we compose. All such text is HTML- escaped before it reaches Google Chat, so a summary containing angle brackets or an ampersand cannot alter the structure of the message or inject markup into someone else's chat client. Comment bodies are parsed structurally from Atlassian Document Format rather than string-matched, which is also why mentions resolve to names instead of raw identifiers.

Both behaviours are covered by automated tests that run before every release, specifically so a future refactor cannot quietly remove them.

4. Secrets

A Google Chat webhook address is a bearer credential: anyone holding it can post into that room. We treat it as one.

Before every save we verify that the address is a Google Chat webhook — the host must be chat.googleapis.com. This prevents a misconfiguration, or a malicious administrator, from turning the app into a generic outbound data channel.

5. Logging

Platform logs contain counters and outcomes — how many rules were evaluated, how many matched, whether a delivery succeeded — and issue keys. They do not contain issue content, comment text, webhook addresses or any other secret. The delivery log shown on the settings page records the rule name, the issue key, the time and the result.

6. How changes reach you

Every release passes the same gate, run as a single command before any deployment:

What is missing, stated plainly: there is no second pair of eyes. With one maintainer there are no pull requests and no peer review, and no automated gate fully replaces that. The compensating controls are the ones above plus the narrow design in section 1 — a mistake in our code cannot reach a host we never declared, or write to a Jira we have no permission to write to.

7. Dependencies

Runtime dependencies are checked with npm audit --omit=dev before release. At the time of writing this reports nine advisories, all of them inside Atlassian's own @forge and @atlaskit packages, which we consume as published and cannot patch independently. None is reachable from a code path our app executes. We re-check on every release and take Atlassian's updates as they ship.

We do not publish a software bill of materials. The standard tooling (npm sbom) currently fails to resolve the dependency graph of Atlassian's own packages, and we would rather publish nothing than publish a manifest we know to be incomplete.

8. Reporting a problem

Email security@winnowtools.com. Please include enough detail to reproduce the issue, and please give us time to fix it before disclosing it publicly. Our security page sets out response times and the safe-harbour terms for good-faith research.

9. Incident response

What counts as an incident

Any of the following: a vulnerability reported in our app; evidence that data reached somewhere it should not have; loss or suspected loss of a credential we hold, including an Atlassian API token; or notice from Atlassian or Google of a problem affecting our app.

Who responds

One named person, reachable at security@winnowtools.com, which is also the security contact registered with the Atlassian Marketplace. We keep that address monitored; an unreachable security contact is treated as an incident in itself, because it is one.

Steps

  1. Acknowledge — within 2 business days, to whoever reported it.
  2. Contain — if a credential may be exposed, rotate it immediately. If a released version is actively harmful, we ask Atlassian to pull it rather than wait for a fix.
  3. Assess — reproduce it, score it with CVSS, and determine which customers and what data are affected. Written down as we go, not reconstructed afterwards.
  4. Fix — with a regression test that fails before the fix and passes after it, so the same defect cannot return unnoticed.
  5. Notify — Atlassian, through the process in their app security incident guidelines, and affected customers directly at the contact we have for them. If we are unsure whether you were affected, we will tell you that rather than stay silent.
  6. Review — after the fix ships, write down what allowed it and what changes. If the answer is a new automated check, it is added to the release gate.

Timelines

We work to Atlassian's Marketplace Security Bug Fix Policy for cloud apps, which sets these deadlines from the point a vulnerability is reported or triaged:

Critical (CVSS ≥ 9.0)fixed within 10 days
High (CVSS ≥ 7.0)fixed within 4 weeks
Medium (CVSS ≥ 4.0)fixed within 12 weeks
Low (CVSS < 4.0)fixed within 25 weeks

These are ceilings set by Atlassian, not targets of ours. A small app with a small codebase has no good reason to use the whole window, and we do not plan to.

10. What we do not claim

No external penetration test. No SOC 2 or ISO 27001 certification. No paid bug bounty. No 24/7 rota — we are one person in one time zone. No anomaly monitoring beyond the platform's own logs, and no twelve-month log retention.

If your procurement process requires any of these, write to security@winnowtools.com and we will tell you honestly where we stand. A vendor who answers "yes" to everything on a questionnaire is telling you something, and it is not what you hoped.