Policy
Security Policy
Applies to: Approval Flow for Jira and Attestation Flow for Confluence, the Atlassian Forge apps published by Stanki Guild sp. z o.o.
Effective date: 12 October 2026 · Last updated: 12 October 2026
1. Reporting a vulnerability: start here
#Email security@stankiguild.com. Please include: the app and version, what you did, what happened, and why you think it is a security problem. A proof of concept helps enormously.
| Stage | Our target |
|---|---|
| Acknowledgement that a human has read your report | 2 business days |
| Initial assessment, severity, and what we intend to do | 5 business days |
| Fix and deploy | per the severity table in §8 |
| Status updates while it is open | at least every 10 business days |
Safe harbour. If you research in good faith, stay within your own test instance, do not access or modify anyone else's data, do not degrade the service, and give us reasonable time to fix the issue before publishing, we will not pursue any claim against you and we will credit you if you wish. We do not run a paid bug bounty (see §11).
Do not report a vulnerability to support@stankiguild.com or in a public forum. Use security@stankiguild.com.
2. Architecture: what there is to attack
#Both apps are Atlassian Forge apps, built to meet the Runs on Atlassian criteria. The security-relevant facts follow from that, and they are enforced by the platform rather than asserted by us:
- We run no infrastructure. No servers, no containers, no databases, no object storage, no message queues, no CI-hosted runtime. Every line of app code executes in Atlassian's Forge FaaS sandbox, on Atlassian's compute, in Atlassian's network.
- No egress. Neither manifest declares
permissions.external,remotes, or a web trigger. There is no outbound network path. This is verifiable from the published manifest. - No third-party services. No analytics, no telemetry, no error reporting, no feature-flag service of our own, no CDN, no external fonts or scripts. Nothing to compromise upstream of you.
- No secrets. The apps hold no API keys, no tokens and no credentials of any kind: there is nothing for them to authenticate to. Nothing is stored with
storage.setSecretbecause nothing needs to be. - All storage is Atlassian's. Data lives in Forge hosted storage (the Forge Key-Value Store), scoped to your installation on your site. See the Privacy Policy for the exact records.
- Transport, encryption at rest, security headers, patching of the runtime and network isolation are Atlassian's responsibility and are provided by the Forge platform. We do not implement, and cannot weaken, any of them.
3. Authentication and authorisation
#Who you are is decided by the platform, never by the browser. Every resolver entry point derives the acting user's Atlassian account ID from the trusted Forge invocation context (context.accountId, falling back to context.principal.accountId for function-style contexts). The client-side payload is treated as untrusted input throughout: it can carry what to do, never who is doing it.
The same rule applies to the object being acted on. The Jira issue key and the Confluence page ID are taken from the trusted context, not from the request body, so a crafted payload cannot address a record belonging to another work item or page.
Everything the apps write is keyed to that trusted context. A sign-off receipt in Approval Flow and an acknowledgement receipt in Attestation Flow both record the account ID the platform supplied: a user cannot sign off, acknowledge or attribute an action as someone else. The one deliberate exception is Approval Flow's out-of-office delegation, where the receipt records both identities (by the substitute, onBehalfOf the delegator) and the delegation itself was created earlier by the delegator from their own trusted context.
Product-level authorisation. The apps do not attempt to re-implement Jira or Confluence permissions. Users only reach an app surface that the host product already decided to render for them, and the app reads only within the scopes the site administrator consented to at install. Where the app enforces its own rule on top (Approval Flow's option to restrict configuration to project administrators), it verifies it against Jira itself, calling GET /rest/api/3/mypermissions?permissions=ADMINISTER_PROJECTS for the current user before allowing the change.
Tenant isolation. Forge hosted storage is scoped per app installation. The apps construct storage keys only from values in the trusted context; no code path builds a key that could address another site's installation.
4. Scopes, and least privilege
#| App | Scopes |
|---|---|
| Approval Flow for Jira | storage:app, read:jira-work, read:jira-user |
| Attestation Flow for Confluence | storage:app, read:confluence-user, read:confluence-content.summary, read:page:confluence, read:confluence-groups |
Every scope maps to endpoints the app actually calls; there are no scopes held "just in case". Note in particular:
- Attestation Flow holds no write scope at all in either product. It reads page metadata and never the page body.
- No app requests any scope that would let it read a Jira issue description or comments, or a Confluence page body.
Pre-publish scope lock. The scope sets above were fixed before first publication, because adding a scope after publication forces every existing customer's administrator to re-consent. We would rather ask once, in the open, than surprise an administrator later.
5. asApp(): what it is used for and how it is constrained
#Forge offers two ways to call Atlassian's APIs: asUser(), acting with the current user's permissions, and asApp(), acting with the app's own installation permissions. Atlassian's security requirements say an app must default to asUser() and, before using asApp() on an action that requires user-specific permissions, must verify those permissions through the permissions REST API.
Our position, stated precisely:
asApp() is used only for reads of reference data and for the app's own records, never to perform a user-specific, permission-sensitive action on a user's behalf. Concretely, the asApp() calls do exactly these things:
| What it calls | Why it is not a user-permission decision |
|---|---|
Resolve an account ID to a display name (GET /rest/api/3/user, GET /wiki/rest/api/user) | Cosmetic. The account ID is already in a record the user is looking at; the call turns an opaque ID into a readable label. Fail-open: an unresolved ID is simply shown as an ID. |
Read a work item's issue type (GET /rest/api/3/issue/{key}?fields=issuetype, GET /rest/api/3/issuetype) | Reference data used to decide which template or policy to seed. Carries no user content. |
Read group membership and project-role actors (GET /rest/api/3/user/groups, /groups/picker, /project/{key}/role) | Used to answer "is this person an eligible approver?": an input to the app's own eligibility rule, not a grant of any product permission. |
Read Confluence page metadata (GET /wiki/api/v2/pages/{id}) and labelled-page lists (GET /wiki/rest/api/label) | Title, version number and space ID only. The page body is never requested. |
Expand a Confluence group's members (GET /wiki/rest/api/group, /group/{id}/membersByGroupId) | Turns "this group must acknowledge" into "these account IDs are outstanding". Read-only. |
| Write the app's own storage records | These are the app's own data, keyed to the trusted context. |
Where the app enforces a permission of its own, it asks the product: Approval Flow calls GET /rest/api/3/mypermissions?permissions=ADMINISTER_PROJECTS for the current user before allowing an administrator-locked configuration change; that is the permissions REST API the requirement names.
Approval Flow's project-level roll-up (GET /rest/api/3/search/jql, POST /rest/api/3/search/approximate-count) does not use asApp(): it runs as the current user (asUser()), so Jira itself decides which work items the report may include, and the report can only contain work items the viewer could already open.
6. Input handling
#Everything arriving from a browser is treated as untrusted. All user-supplied text is normalised and length-capped before it is stored (notes, requirement text, rejection reasons, template names, identifier lists), records loaded from storage are re-validated on read so that a corrupted or hand-edited value cannot change behaviour, and no code path evaluates user input as code. The apps spawn no child processes and execute no shell commands; a Forge app cannot.
Rendering is done through Atlassian's UI Kit, which escapes content by default.
7. Logging
#The apps log operational events only: what happened, how many records were affected, and errors. The text our code writes to the logs contains counts, work item keys and page IDs, and never account IDs, names, page or issue content or user-typed text. When a platform call fails, we also log the error it returned. Forge app logs are shared with us by default, as with every Forge app; your site administrator can turn this off in Atlassian Administration and can download the logs. We do not export them anywhere.
8. Dependencies and patching
#- Dependencies are almost entirely Atlassian's own published packages (
@forge/*,@atlaskit/*) plus their transitive tree. We add third-party packages sparingly and prefer none. - We run
npm auditon every change and before every release, and keep the output. We review advisories against the actual code path rather than treating a severity number as the whole story, and we say so if a finding is a false positive rather than quietly dismissing it. - We upgrade forward, never force-downgrade. Where an automated fix would resolve an advisory by downgrading an Atlassian package to an older major version, we do not take it; we wait for or request the forward fix and document the exposure in the meantime.
- The apps run on the
nodejs22.xForge runtime: current, not end-of-life. Runtime patching is Atlassian's. - Where an advisory originates in a package Atlassian ships and no forward fix exists yet, we disclose the dependency path to Atlassian during review rather than waiting to be asked.
Remediation targets. We follow Atlassian's Marketplace Security Bug Fix Policy for cloud apps, which is the standard we are held to:
| Severity | Fix deployed within |
|---|---|
| Critical | 10 days |
| High | 4 weeks |
| Medium | 12 weeks |
| Low | 25 weeks |
Source: https://developer.atlassian.com/platform/marketplace/security-bugfix-policy/
9. Development practice
#- Both apps are covered by an automated test suite that must pass before any release; the suites currently total over 300 tests, concentrated on the authorisation, quorum, separation-of-duties, version-binding and purge logic: the parts where a bug is a security bug.
forge lintmust be clean before deployment. Among other things it verifies that every REST route the code calls is covered by a declared scope.- Changes are reviewed before release, and releases go to a development environment first.
- We hold no production credentials for any customer system, because there are none to hold.
10. Incidents
#If we become aware of a security incident affecting one of our apps or the data it processes:
- We contain it first (including, if that is what it takes, pulling the affected version).
- We notify Atlassian within 48 hours, as the Marketplace Partner Agreement requires.
- We notify affected customers without undue delay, with what we know, what we do not yet know, what data was involved and what we are doing. Where we act as processor and the incident is a personal-data breach, this is the notification you need under Article 33(2) GDPR in order to make your own regulatory notification: you are the controller and the clock is yours, so we will not sit on it while we perfect the wording.
- We publish a post-incident summary once the issue is closed, unless doing so would put customers at further risk.
- We register at least one security contact on
ecosystem.atlassian.netso that Atlassian Marketplace Security tickets about our apps reach a monitored human.
Report a suspected incident to security@stankiguild.com.
11. What we do not have
#We would rather you learn this here than assume otherwise:
- No ISO 27001 certification. No SOC 2 report. No CSA STAR / CAIQ submission. We hold no third-party security certification of any kind, and we will not imply one.
- No independent penetration test of these apps has been commissioned. We have red-teamed them ourselves and fixed what we found; that is our own work, not an external assurance.
- No paid bug bounty programme. We are not enrolled in the Marketplace Security Bug Bounty Program. Reports to security@stankiguild.com are welcome and will be credited, but not paid.
- No Cloud Fortified badge, which depends on the bug bounty above.
- No 24×7 staffed security operations centre. Vulnerability reports are acknowledged within 2 business days, during business hours (09:00-17:00, Monday to Friday, Europe/Warsaw); see the response targets in §1. A suspected active security incident reported to security@stankiguild.com is answered within 24 hours, including outside business hours. A 24×7 emergency contact is registered with Atlassian for platform-level incidents.
- No customer-facing audit log of our own actions, because we take no actions inside your site: everything is done by your own users, and the apps record who did what.
If any of this changes, this page changes with it.
12. Contact
#| Purpose | Contact |
|---|---|
| Security vulnerabilities and incidents | security@stankiguild.com |
| Privacy and data protection | see Privacy Policy |
| Product support | support@stankiguild.com |
| Postal | Stanki Guild sp. z o.o., ul. Adama Mickiewicza 13/4, 82-300 Elbląg, Poland |