Security — Crosstab
A security policy for an app with no servers is a short document, and pretending otherwise would be the first thing wrong with it. Crosstab runs entirely inside Atlassian's platform. There is no backend, database or network of ours. There is nothing to breach that is not Atlassian's own infrastructure.
Last reviewed: 26 October 2026. This is version 3, in force since the app was listed on the Atlassian Marketplace. A change to how the app is built or operated takes the next version number; a correction that touches none of that moves this date and the digest at the foot of the page instead.
This page is about how the app is built and operated. The one source for what is read, stored and kept, and for how long, is the privacy policy. The whole arithmetic is on the method page.
At a glance
- Your browserthe gadget's interface, in an iframe Jira draws on the dashboard.
- A Forge function, inside Atlassianreads Jira as the person looking, or as whoever switched a schedule on, and hands back totals and labels.
- Your Jira site and the app's storageit reads Jira and writes only to its own storage, and Jira keeps a gadget's settings; counts and a line of sizes go to Atlassian's console, and once a week the account ids it stores go to Atlassian's privacy reporting.
- Anywhere elseout of reach. The manifest declares no external host, and the platform refuses one that is not declared.
| The question | The short answer |
|---|---|
| Where it runs | Inside Atlassian's platform. No backend, database or network of ours |
| Whose permissions | The person looking at the dashboard; for a schedule, whoever switched it on; for a published grid, whoever published it |
| What it may ask for | Twenty-four read scopes, one storage scope, one that checks a search and one that reports account ids, printed below |
| A person as an axis | Refused, against the schema of every field your site returns |
| Data residency | Stored only in Forge storage, which resides where your site does. The function usually runs there too, but Forge does not promise it; nothing leaves Atlassian either way |
| Encryption | Atlassian's, at rest and in transit; the app adds no cryptography of its own |
| Checked by Atlassian | Eligible for Runs on Atlassian by Atlassian's own check, run by the Forge CLI after a deploy. It passed on 8 October 2026 for version 2.0.0 as deployed to the production environment. The badge itself comes with the listing |
| Before it ships | npm audit, lint, a build and the test suite, on the workstation before every push and again in Bitbucket Pipelines on every merge to main and every release, and the browser suite on main before "Tag a release" is offered |
| A vulnerability | support@chmut.com or Atlassian; a critical one fixed within 10 days |
| Who stands behind it | One person, a sole trader, and no team. If the one person stops |
For whoever approves the app
- It reads your Jira, with the permissions the table above names, and nothing else. Every item, and when it is read, is on the privacy page.
- It writes only to its own Forge storage, and a gadget's settings, which Jira keeps on the dashboard when somebody saves the form. In the reader's browser it leaves two small notes, under keys that carry no search: the privacy page says what they hold. It creates, edits or deletes no issue, filter or field.
- It reaches your Jira site and Atlassian's platform. The manifest declares no external host.
- Three scopes look broader than they are.
read:user:jiraandread:group:jiracome in the set Jira asks for to read a saved filter's name; no group is read, and of a person the app asks only a display name.read:board-scope.admin:jira-softwareis asked only for a board's columns: which statuses each holds, and in what order. - One neither reads nor writes:
validate:jql:jira, to check a search when the settings form stores it. - One is asked only on the administration page:
read:permission:jira, to check that whoever uses it holds Administer Jira. - One is asked as the app:
report:personal-data, to send Atlassian, once a week, the account ids the app stores and learn which accounts are closed. A closed account's schedules and published grids are deleted, and its id comes off the setups it saved. - One hourly allowance serves every site. Jira meters the app, not your site: every installation reads from one pool of 65,000 points an hour. Each site's share is capped, but when the pool runs out, every gadget on every site stops reading until the next UTC hour. What it spends of Jira's allowance.
- Publishing is off from the day it is installed, until a Jira administrator turns it on.
- Keep automatically is switched on for the site from the day it is installed: nobody's grid is kept on a schedule until they switch one on, and an administrator can turn it off for everybody.
- Keep a first grid for me when I save is ticked on the settings form of the Crosstab grid gadget, wherever those settings have no kept grid yet; the other four gadgets do not offer it. Saved without unticking it, the gadget keeps its first grid the first time whoever saved it opens it, if they have room for another; its settings hold that person's Atlassian account id.
- A grid kept automatically is read as its owner, with their permissions, on its schedule, whether or not anybody has the dashboard open: that is what
allowImpersonationon the read scopes allows. - After uninstalling, Atlassian keeps the app's storage for 28 days, then deletes it. Every record's own lifetime is in Retention.
What you can check yourself
The source is not public, so every claim below is narrower than it sounds. These you can check yourself, before installing.
| What | Where to check it | What it shows |
|---|---|---|
| The manifest | The consent screen | No external host |
| The scopes | The consent screen | What the app may ask Jira, line by line |
| What runs when nobody is looking | The manifest's triggers | One thing, for the people who asked for it |
| Runs on Atlassian | The badge, which comes with the listing | Atlassian's own check of where the code runs and the data stays |
The manifest
Atlassian shows it to you on the consent screen. A Forge app declares every external host it may reach. A host it did not declare it cannot reach: the platform refuses the request. This app declares none.
The scopes
There are twenty-four read scopes, one storage scope, one that checks a search and one that reports account ids. They are granular rather than one blanket read:jira-work. That is more lines to read, not fewer, and it is the point. A granular list can be checked against what the app claims to do, and read:jira-work cannot.
Some of the length is Atlassian's bundling rather than ours. Reading a saved filter's id and name costs nine scopes. Among them are read:user:jira and read:group:jira, in a gadget that will not draw an axis by person. The same four person-shaped scopes are what Jira asks for a display name. That is the one thing about a person this app asks for. A worklog arrives with its author and comment, and each change in an issue's history with its author. The app uses only the time logged and the changes to the sprint, the status and the field being added up, and stores none of it.
Every scope is printed below, with why it is asked for.
What runs when nobody is looking
One thing, and only for the people who asked for it: Keep automatically, beside Keep this. It keeps a grid every week on a chosen day, on the 1st of each month, or whenever a sprint closes. Each is kept then, or as soon after as Jira allows.
| Entry point in the manifest | What it does |
|---|---|
| A scheduled trigger, once a day | Puts a run on the app's own queue |
| An event trigger, a sprint's close | Puts a run on the app's own queue |
| That queue's consumer | Reads, one run at a time |
The consumer does one narrow thing for each schedule on the site whose turn it is. It reads the grid as the person who switched that schedule on, by their account id, with their permissions and no one else's. Then it does one thing more: keep it into their own history, exactly as their own press of Keep this would.
- Nothing is read as the app, and nothing is published.
- A schedule stops, rather than reading as anyone else, once Jira will no longer act as its owner.
- At most twenty schedules run on a site, and at most three of any one person's.
- The platform skips both triggers on a site without an active licence.
- A Jira administrator can turn it off for the whole site, on Crosstab's administration page, beside the publishing setting. While it is off, no schedule runs and nobody can switch one on. Nothing is deleted. Once it is back on, no week or month whose day came before that day is kept, and no sprint that closed while it was off.
- A Jira administrator can switch off one person's schedule on the same page, to free a place on a full site. For each running schedule the page shows them its owner, by the name Jira shows them, the filter's name if they can open that filter, when it keeps, when it was switched on and when it last kept - never its settings, its search or anything it kept. The schedule is stopped, not deleted, and its owner's gadget says a Jira administrator stopped it.
- A Jira administrator can delete a stopped schedule on the same page: one they switched off, or one Jira stopped because it would no longer let the app act as its owner. Stopped schedules are listed after the running ones. Deleting one takes its owner's account id and settings out of the app's storage. The grids it already kept stay.
What a scheduled read asks of Jira beyond a render is listed on the privacy page.
Everything else reads Jira only while somebody is looking at the gadget or pressing something on it. The privacy page lists each moment. One script runs beside the gadgets. It shares a first read between the ones on a dashboard asking the same question. It is part of that dashboard: it runs while the dashboard is open and reads as whoever opened it. It keeps nothing a gadget's own read would not.
That is a statement about the manifest in front of you, not about every release after it. If a release adds another trigger, this page and the privacy policy say so before that release ships.
How much of Jira's allowance it may spend
Jira meters every installation of this app out of one pool of 65,000 points an hour. So a read made while nobody is looking spends what readers on other sites need. One site's share is capped: at most three tenths of the pool, schedules and gadgets together, so three sites at their ceiling still leave a tenth for every other site.
| Who spends, on one site | At most, in any UTC hour | Past that |
|---|---|---|
| The schedules | 6,500 points - a tenth | A schedule that does not fit waits for the next hour rather than failing |
| The gadgets people open, their settings forms included | 13,000 points - a fifth, give or take 500 points for each gadget reading at the same moment | A gadget asks Jira nothing until the hour turns, and says that the limit is this app's, not Jira's |
- Both are priced the same way, and counted apart. A point per issue and a point per page, and two points for each count Jira is asked instead. These prices were measured on 30.09.2026. The schedules' share keeps a margin for the calls around the read.
- A background read takes at most five thousand issues.
- The first refusal from Jira stops every schedule on the site. That is the first
429. They stop until the hour turns, or for as long as Jira asked if that is longer. So does any answer saying the pool is running low. - A grid Jira cut short that way is not kept.
- One run reads at a time, through one queue.
Checked by Atlassian
The Runs on Atlassian programme asks three things of an app.
| The programme asks for | This app |
|---|---|
| Atlassian-hosted compute and storage only | The manifest declares no external host |
| The host's own data residency | Stored only in Forge storage, which resides where your site does |
| A customer able to control external egress, such as analytics and logs | What leaves the function is counts per minute into Atlassian's own counters and a line in Atlassian's own log. A site administrator can turn off Custom metrics access and Logs access |
The counters and the log hold sizes, status codes and fixed phrases, never content. The privacy page describes both.
Atlassian's own check agrees. The Forge CLI runs it after a deploy, and it passed on 8 October 2026 for version 2.0.0, as deployed to the production environment. Atlassian applies that label to an eligible app; the vendor does not claim it. It is not an audit. It certifies nothing about how the code is written. It says where the code runs and where the data stays. That is what the manifest says, checked by the platform the app runs on.
The twenty-seven scopes, printed
Named here rather than left on a consent screen the reader has already clicked through. The grouping is the manifest's own. The right-hand column is why each group is asked for. Every read scope but read:sprint:jira-software and read:permission:jira carries allowImpersonation. That is what lets a grid kept automatically be read as its owner. The sprint scope reads nothing and is held for the event alone. The permission scope is asked only as the person at the administration page.
| Scopes | Asked for |
|---|---|
read:field:jira · read:field-configuration:jira · read:project-category:jira | The field description: what may be an axis, what may be a value, and the names behind saved settings |
read:filter:jira · read:jql:jira · read:application-role:jira · read:group:jira · read:user:jira · read:project:jira · read:project-role:jira · read:issue-type-hierarchy:jira · read:avatar:jira | Saved filters: the picker, and naming the one already chosen. Nine scopes for two fields, id and name, because Jira counts them as a set. It reads no group. It reads a person only for a display name: the reader's own, a publisher's by account id, and a schedule owner's by account id for a Jira administrator on the administration page |
read:issue-details:jira · read:priority:jira · read:field.default-value:jira · read:field.option:jira | The issues themselves. The search is a POST rather than a GET. Atlassian gives the two verbs different sets for the same question, and GET's includes read:audit-log:jira |
read:board-scope:jira-software · read:board-scope.admin:jira-software | The order a board draws its statuses in, which no issue carries and no Jira Platform endpoint has. Asked for only when an axis is status or board column |
read:instance-configuration:jira · read:issue.time-tracking:jira | How this site writes a length of time: how long a working day is, and which of Jira's three forms a duration is drawn in. Asked when the settings form opens, and when the metric is a length of time - one of the six fields held in seconds, time between two dates, or time logged |
read:issue-worklog:jira · read:issue-worklog.property:jira | The worklogs of one issue, for time logged in a month. The search carries at most twenty worklogs an issue, and the rest are asked for issue by issue. Asked for only when the metric is time logged |
read:sprint:jira-software | The event a sprint's close arrives as, for a grid kept when a sprint closes. The platform delivers it only to an app holding this scope. Nothing is read with it. The sprint's issues come through the search above, as the schedule's owner |
read:permission:jira | Whether whoever uses the administration page holds the Administer Jira global permission. Asked on every change made there and each time it lists the site's schedules, as the person at the page, and on a production site nowhere else. One yes or no crosses into the app |
validate:jql:jira | Checking a search when the settings form stores it. Jira's strict parse is the one question that names a field it does not know. Not a read and not a write. Asked only by the person at the form. A site that refuses it has the search checked as before |
storage:app | Keeping a grid, by hand or automatically, saving a setup, holding a reader's last grid for five minutes, each reader's finished work as totals, and the other records the privacy page lists. The only scope here that writes anything |
report:personal-data | Sending Atlassian, once a week, the account ids the app stores - a schedule's owner, a published grid's publisher, a saved setup's author - so it can say which accounts are closed. A closed account's schedules and published grids are deleted, and its id comes off the setups it saved. The only scope asked as the app rather than as a person. Neither a read of Jira nor a write to it |
validate:jql:jira exists because a misspelt field in a search answers with no issues rather than an error. It reads nothing and writes nothing.
Two of these are worth a second look rather than a skim:
read:user:jiraandread:group:jiraare in a gadget that will not draw an axis by person.read:board-scope.admin:jira-softwarehasadminin its name and buys a column order. A site that refuses the two board scopes gets the statuses in the order they were added to the site. That is how Jira itself sorts them.
Where the data lives
Nowhere it was not already. The code runs in a Forge function inside Atlassian's infrastructure. The interface runs in an iframe in the reader's own browser. The function turns the issues it reads into totals and hands them to that iframe. There is no server or database of ours for anything to be written to.
What is kept sits in your own site's app storage, inside Atlassian's infrastructure and under your agreement with them. None of it holds an issue key, a description or a comment. The one piece of ticket text the app keeps is the label on the Parent axis: a parent's own summary, usually an epic's.
That storage is Forge's own. Atlassian's rule is that an app keeping all end-user data in Forge hosted storage qualifies for PINNED data residency, with nothing to configure. This app keeps it nowhere else, so the storage sits where your site's data is pinned. Where the function runs is a separate question. Forge usually runs it in your site's region, but Atlassian does not guarantee that, and this page does not either. Either way, the app calls no host outside Atlassian's: the manifest declares none.
Every record is listed once, on the privacy page:
| Question | Where it is answered |
|---|---|
| What is read, when, and why | What the app reads |
| What each record holds | What the app stores |
| How long each record lives | Retention |
| What reaches the vendor | What the vendor sees |
Uninstalling the app removes all of it, on Atlassian's schedule rather than ours.
Whose eyes the data is read with
The search runs with the permissions of whoever is looking at the dashboard. It does not run with an application account, nor with the permissions of whoever configured the gadget.
| Grid | Read with the permissions of | Who can open it |
|---|---|---|
| On a dashboard | Whoever is looking | That person. Two people opening the same gadget can see two different grids |
| Kept by hand | Whoever kept it, when they pressed | Only them. The storage key is a hash of their account id and of the choices |
| Kept automatically | The one person who switched that schedule on, named by their account id | Only them. It is kept only into their history |
| Published | Whoever published it, on the press | Everybody who opens that gadget |
- A grid read for somebody is never wider than their own view of Jira. A reader who cannot see a project does not see its issues in a total.
- A schedule is never read as the app, and never as anybody else. It stops, rather than reading as anyone else, once Jira will no longer act as its owner.
- A colleague opening the same gadget on the same dashboard sees their own kept grids, never somebody else's. The interface says so where the button is.
- A saved setup reaches the whole installation. It is a name and four ids, and no figures at all. Everybody reads it; only whoever saved it can change or remove it.
- A reader with no account, on a publicly shared dashboard, is handed no kept grid and no setup.
A kept grid is a picture
A kept grid is a picture, not a query, and so is a published one. It holds figures already worked out. So drawing it again does not re-check anybody's permissions against the issues it counted. That is the exception a reader should know about.
A published grid
A published grid is the one picture meant for other people. Publish this grid leaves one grid on that gadget for whoever opens it. A colleague then sees figures from the publisher's view of Jira. That includes issues their own permissions may not show them.
So it is never silent. The grid is drawn under a banner naming whose view it is. The gadget asks before each publish, except when the publisher's own published grid is the one showing on that gadget.
- Only whoever published it can replace it or take it down.
- A grid whose publisher has left stays. It is hidden only while a site administrator has publishing turned off.
- Its figures are read from Jira on the press, as the publisher, not taken from the page. So a browser cannot put numbers under somebody's name that Jira never gave them.
- A read that stops before the end of the filter, or that Jira refuses, publishes nothing.
- It holds the publisher's account id, so only they can take it down. The name over it is asked of Jira when it is drawn, not stored.
How a published grid is keyed
It is stored against the gadget rather than a person. The key is a hash of the dashboard and gadget ids. Those ids are taken from the platform, never from the page. So nobody can ask for the grid of a dashboard they were never shown.
The publishing switch
A site has to want this before anybody can publish.
- The setting: Let people publish a grid, on the Crosstab page under Apps in Jira's administration. It is off until somebody turns it on.
- Off: the resolver refuses every publish and offers no published grid. The gadget draws no publish button.
- Who can change it: site administrators. The change is accepted only from that administration page, which Jira shows to site administrators alone.
- A setting that cannot be read counts as off.
How the switch is enforced
The resolver reads which module called it from the platform's context, not from the request. So a request from anywhere but the administration page cannot change the setting. And it asks Jira, as the person at the page, whether they hold the Administer Jira global permission. A no, or no answer, changes nothing. The same two checks guard the Keep automatically switch, the list of the site's schedules, switching one of them off and deleting a stopped one.
No person as an axis
No axis can be a person. Not assignee, not reporter, not creator, not any user field. This is enforced against the schema of every field your site returns, not against a list of names. So a custom user picker called Reviewers is refused for the same reason Assignee is. So is one that holds several people at once.
The rule enforces the shape of a field. Three things fall outside a shape: a label named after a colleague, a custom select list whose options are typed-in names, and a line of JQL. All three are set out on the privacy page, rather than left for a reviewer to find.
Checks before release
The gate is npm run check, run on the workstation before every push. Its first step is npm audit. Bitbucket Pipelines runs the same checks on every merge to main, and again on every release before it offers a deploy. A known vulnerability in a dependency stops the change on the workstation, and a release that fails the audit offers no deploy. A pull request gets the same checks without the audit and the test suite. On main the browser suite, npm run check:ui, runs too, by its own manual step, and "Tag a release" is offered only once it has passed on that commit.
| Check | Detail |
|---|---|
npm audit at moderate and above | A known vulnerability in a dependency stops the change, rather than appearing in a report nobody opens |
npm audit of the deploy tools' own lockfiles | Reported, not a gate. What it found, and why it is accepted, is under who can deploy |
| Formatting, and lint at zero warnings | - |
| Only the four files allowed to reach the platform do | - |
| A build, before the suite rather than after it | The bundle assertions have nothing to look at otherwise |
| The test suite | Over 4,600 tests across more than a hundred files |
Two of those tests are about this page's claims rather than about behaviour.
- One reads every built file. It fails on any host the bundle reaches that is not this site's own. The handful it permits are strings inside error messages. Each is checked by reading the characters around it, not by trusting the name.
- The other holds the claims in these documents against the code that has to make them true.
Who can deploy
A Forge deploy reaches every installation with no action by you. That is the app's most valuable asset and its largest risk. Every deploy runs in Bitbucket Pipelines, as bitbucket-pipelines.yml declares it, and only from a release tag.
| What | How |
|---|---|
| A release | A v tag, which the "Tag a release" step cuts from main after the checks there pass, the browser suite included. The tag's pipeline runs the checks again on its commit before it offers a deploy |
| Development, and these pages | Deployed from the tag, each by its own manual step, or again, after the checks, in the pipeline someone runs to approve a major version |
| Production | A manual step too, offered only after development and the pages have deployed the same commit |
| A change of scopes | A new major version: a site keeps the previous version until its admin accepts the new scopes. Development approves it by itself, and its installation is upgraded only when someone runs a custom pipeline for that release. Production approves it only when someone runs a custom pipeline for that release, which deploys development and these pages again first; the usual production step fails on it and deploys nothing. Until production has its first installation, Forge asks for no approval, and the usual step deploys it like any release. Either can be started from any branch or tag, but deploys only a commit on main that a release tag points at |
| Any branch or pull request | Nothing deploys |
| The deploy credentials | Secured variables in Bitbucket, never in the repository. Each belongs to the deployment that uses it, so only that step can read it |
| The check steps | Hold no credentials at all |
| Rolling back | Redeploy of the previous release, from Bitbucket's deployment history. The step rebuilds from that release's own commit. A Redeploy of a production deploy that approved a major version approves it again |
What the deploy steps do not run
Each deploy step installs its dependencies with lifecycle scripts disabled, both the app's and the tools'. The Forge CLI is 832 packages with three install scripts, one of which downloads a 38 MB executable during install. Running arbitrary install code in the one environment holding a deploy token is the risk that flag exists for.
Each deploy step takes its tools from a lockfile of its own, committed beside the deploy script, and restores no npm cache. The lockfile fixes the Forge CLI, the tool that deploys these pages and every npm package under them, so a release of any of them cannot change a deploy on the day it ships. The check steps install with scripts on and keep an npm cache; nothing they leave in it reaches a step that holds a credential.
The tools' own trees are audited by npm run check too, from their lockfiles, and it reports what it finds rather than failing on it. On 8 October 2026 that was 44 known vulnerabilities in the Forge CLI 14.1.0 (3 critical, 29 high, 8 moderate, 4 low) and none in wrangler 4.148.0: the three it had, all in the image library its local simulator pins, were cleared by taking that library's next patch release. That override comes off once wrangler pins that release or a later one itself. The Forge CLI's are accepted rather than fixed: 14.1.0 is the latest release, and it ships its own npm-shrinkwrap.json, so its tree changes only when Atlassian releases a new one. Overriding packages inside it would deploy with a tree Atlassian has not tested. The deploy steps move to each new Forge CLI release, and the count here is measured again when they do.
The build still runs the code of every build dependency, and what it builds is what is deployed. The step builds with no Forge or Cloudflare variable in the environment it hands the build, so a dependency that reads its own environment finds no token there. That is all it covers: code in the same container, run as the same user, can still read the step's own environment from /proc. A dependency that could do that could as well change the bundle it builds, which is why the app's dependencies are audited and locked.
The deploy steps run on one Node image, pinned by its digest. A change to the image's tag reaches them only through a commit that names a new digest.
A deploy runs the pipeline file of the commit it deploys. So all of this holds for releases tagged since the pipeline has done it, and a Redeploy of an older release runs that release's own steps.
The repository has one account with write access, the vendor's. On this Bitbucket plan a deployment cannot be tied to a branch or a tag by a setting. The pipeline file does that, and only that account can change it. A tag on a commit that is not on main deploys nothing when that commit's pipeline file carries the check, which each deploy step runs before it installs anything. It stops a mistaken tag. A commit whose pipeline file lacks the check, an older branch or one that removed it, is not stopped by it, so what remains is that only that account pushes tags and a person starts each deploy. That account could still push a release tag by hand on a commit of main, which skips the browser suite: tags are cut by the "Tag a release" step, not by hand.
The vendor's machine keeps an Atlassian token of its own, because reading the app's logs needs one. It could deploy, but is not used for that.
Reporting a vulnerability
Write to support@chmut.com. Say what you found and how to reproduce it. You do not need a proof of concept for anybody to take it seriously.
You will get a human reply within 48 hours, on the same working days as any other message to support. That is a commitment about acknowledgement rather than about a fix.
You can also report it to Atlassian. They raise it in their Marketplace Security project, where it arrives with a remediation due date. Either route reaches the same person.
The app is held to these timeframes. They are Atlassian's rather than ours.
| Severity | CVSS | Fixed within |
|---|---|---|
| Critical | 9.0 and above | 10 days |
| High | 7.0 to 8.9 | 4 weeks |
| Medium | 4.0 to 6.9 | 12 weeks |
| Low | below 4.0 | 25 weeks |
Response times
Two clocks, and it is easy to read one as the other.
| What | The clock |
|---|---|
| An ordinary question: a figure that looks wrong, a setting that will not do what you want, a grid that came back partial | Answered within 48 hours, between 09:00 and 19:00 Europe/Kyiv, Monday to Friday, except on Ukrainian public holidays |
| A vulnerability | The table above. It is Atlassian's, and measures time to a fix rather than to a reply |
Both reach the same person. That is the whole of the support arrangement. It is written down plainly, because an administrator deciding whether to depend on this is entitled to know how thin it is.
Who stands behind this
One person: Oleksandr Chmut, a sole trader, trading as oc. It is the same vendor as this studio's other Marketplace app. There is no team behind either of them.
That is a smaller answer than an administrator would like. It is given plainly, because the alternative is a page implying a company and a reviewer finding out otherwise.
| What | Held by |
|---|---|
| The response times above | One person |
| The deploy credentials | One person |
| The incident plan below | One person, to run |
It also means there is nothing between a reported problem and the person who can fix it. A vulnerability report reaches the author of the code the same day it is sent, on either route.
If there is an incident
Containment here is unusually direct. A Forge deploy reaches every installation without any action by you. So rolling back to the last good revision is both the fastest response and a complete one. There is no upgrade for an administrator to apply, and no site left behind on a broken build.
What counts
| Incident | Why it is one |
|---|---|
| The app doing something to a site that it is not supposed to be able to do | It is what the rest of this page is about |
| Data of yours reaching somewhere it should not | It is what the rest of this page is about |
| The app unavailable, or drawing wrong figures, across installations rather than on one board | A grid that quietly adds up wrong is worse than a grid that does not draw. Only one of the two announces itself |
How one is noticed
In almost every case, because somebody writes. This is the thin part.
- The app's own refusals go to
console.warnand into Forge's logs. They are read by runningforge logs, and nothing watches them. - There is no alerting and no error aggregation, and this page is not going to imply otherwise.
- Atlassian's own reporting routes arrive by mail.
- The logs are read by hand with
forge logs, after a production deploy. - Counters of what the app spends of Jira's hourly allowance, per site and per minute, are in Atlassian's developer console. Atlassian's own count of the requests Jira refused with
429is beside them. They are kept for fourteen days. Nothing watches them either: the console cannot raise an alert on an app's own counters. - Between deploys nothing reads them. This page does not promise a weekly pass that one person would quietly stop making.
So the clock below starts when a report arrives, not when the fault does.
What happens when
The clocks, and they are what this app commits to you.
| When | What happens |
|---|---|
| Within one working day | The report is answered by the person who wrote the code, saying what is known |
| Within 72 hours | Affected sites are told, if data of yours is involved |
| The same working day | The build is rolled back, once the fault is confirmed and where a rollback is the fix |
| Within 5 working days | A written account of what happened, to anybody who was told |
The 72 hours is the one clock Atlassian recommends rather than one chosen here. The notification goes to the technical contact Atlassian holds for each licensed installation. That is the only address this vendor has for you. There is no other list: this app collects no email addresses of its own. A site that installed the app through a distribution link, before the app was listed, has no such contact and cannot be reached this way.
What is done, in order
- Roll back to the last good revision first, and diagnose afterwards. A rollback is one redeploy of the last good release, it reaches every site at once, and it needs nothing from you.
- Stop deploying anything else, including unrelated fixes. A second change during an incident is how the first one becomes unexplainable.
- Write down what is known and when it was known, as it is learnt rather than afterwards.
- Tell Atlassian within 24 hours of becoming aware of it, as a P1 ticket in the Marketplace Partner Portal, and update them at least every 6 hours until it is over. A vulnerability that nobody has exploited goes to their Marketplace Security project instead.
- Tell the affected sites on the clock above.
- When it is over, write one account of what changed, so that this section is not the same next time.
Who does what
The same person does all of it. That is the arrangement, and what it costs you is worth stating. Nobody is on call. An incident that begins at midnight on a Saturday is noticed on the Monday. One that begins during a week away is noticed when that week ends, and so is any report sent during it. The clocks above start from that.
The compensating fact is the one this section opened with. The fix is a rollback, and it needs no action from you. The person who can do it is the person who wrote the code.
What this plan does not cover
Atlassian's own platform. If Forge, Jira or Atlassian's storage is what failed, the incident is theirs. Your agreement about it is with them. What this vendor can do is tell you what it knows.
If the one person stops
An administrator told there is one person and no team will ask a question. What are they left holding on the day that person stops? A page that admits the first and does not answer the second is only half of an answer.
- Nothing of yours is inside this app. It reads your filters, your issues and your fields, and writes none of them. No issue is created, edited, transitioned or commented on by it. There is nothing in your site that would have to be unpicked. Another tool pointed at the same filter draws from the same place.
- Two things exist only here. A grid you kept is the figures as they were. A named setup is a gadget's settings under a name. Both are in Forge hosted storage on your own site, not on any server of ours. There is no server of ours.
- Both come out while the site is subscribed. Any grid on screen, today's or one kept a year ago, leaves as copy, CSV, Excel or a picture whenever you press it.
- The subscription is the condition of drawing figures at all, kept grids included. So export while one is live, not after it lapses.
- Lapsing does not delete. The storage stays where it is. A new subscription brings the grids back as they were. The gadget says so on the day it stops drawing.
- Uninstalling hands the storage back to Atlassian to remove on its schedule.
- Deleting what you stored is not behind the subscription. Forgetting a grid, forgetting a setup, taking your own published grid down and switching Keep automatically off all work whether or not a site is paying. A site that has stopped paying is precisely the site that may want what it stored gone.
- The code keeps one list of what a subscription is a condition of and what it deliberately is not. The test suite holds it to that list.
- There is no switch at this end. There is no licence server of ours to stop answering, and no host of ours to go dark: the app declares neither. It runs on Atlassian's infrastructure, and the licence check is Atlassian's own. Only a site's own subscription ending stops it drawing figures. Nobody here can reach it.
What stops if the one person stops is the part a person does. That is new versions, an answer to a support message, and eventually a platform change that nobody is here to follow. Nobody else publishes new versions: there is no second maintainer, and no escrow arrangement that would hand the app to one. That is the real loss: a product that stops improving. It is not a report that disappears, and not a number you cannot get back.
What is not claimed
| This page does not claim | What it says instead |
|---|---|
| That the code is correct | It claims the code is checked by the gate above, and that the claims in these documents are held against it. Those are different things, and the difference matters |
| That Atlassian's platform is secure | Everything here rests on it, and your agreement about it is with them |
| That a label is safe | A component named after a colleague is text your organisation chose. No rule in this app can tell it from any other text. It is the one place the axis rule stops short, so the privacy page says it too |
| Independent review | Nobody outside this project has audited it. If that changes, this page will say so with a date |
This edition
The file below is the authoritative text. This page renders it and adds navigation and this block, so the page and the file are not byte-identical — it is the file that governs, and the digest is over the file.
Version 3 — security.md, 57 KB
162c0b9f30ec2f5941e19a43d5296e22acf7d9f1f43488d8523ae77d390f2e54
Check it with shasum -a 256 security.md once the file is downloaded, which prints the digest above and nothing to compare by eye. On Windows, Get-FileHash security.md -Algorithm SHA256 in PowerShell, which prints in capitals — the comparison is case-insensitive. The digest is published by us: it shows the file has not drifted or been altered in transit, and it is not a third-party attestation.