These guidelines are designed to help developers build secure AGC apps by following best practices aligned with FedRAMP controls and Atlassian's security standards. We strongly recommend following these guidelines to protect customer data and maintain their trust.
For mandatory requirements, refer to Security requirements for Atlassian Government Cloud apps. For more information on FedRAMP compliance, refer to Security and compliance on the AGC.
All AGC compatible apps must use Forge's built-in OAuth 2.0 implementation for authentication and authorization with Atlassian APIs. This ensures secure, standardized authentication flows that align with security requirements.
Best practices:
asApp() or appSystemToken methods for actions triggered in user context. These authorization methods possess app-level permissions and elevate privileges, unlike asUser(), which enforces user permissionsExample: Using Forge authentication APIs
1 2import api, { fetch } from '@forge/api'; // Correct: Use Forge's authenticated request method asUser() // This automatically handles OAuth 2.0 authentication on behalf of the user const response = await api.asUser().requestJira('/rest/api/3/issue/PROJ-123', { method: 'GET', headers: { 'Accept': 'application/json' } }); // Incorrect: Do not use credentialed requests const badResponse = await fetch('https://api.atlassian.com/ex/jira/rest/api/3/issue/PROJ-123', { method: 'GET', headers: { 'Accept': 'application/json', 'Authorization': 'Basic ' + btoa('user@example.com:api_token') // Never do this - use Forge's auth methods } });
Do not collect, use, or store Atlassian account credentials (namely, secrets managed by Atlassian identity). This is strictly prohibited:
Ensure strict tenant isolation at runtime. Each tenant's data must be completely isolated from other tenants.
Implementation guidelines:
Example: Tenant-isolated storage
1 2import { kvs } from '@forge/kvs'; // Correct: Forge storage automatically enforces tenant isolation await kvs.set('customer-data', data); // Isolated per tenant // Incorrect: Use global variables or shared state global.sharedCache = {}; // Never do this - violates tenant isolation
Failure to maintain isolation could result in accidental data leakage or unauthorized access, leading to a loss of customer trust.
Do not execute arbitrary code or spawn external processes. All code execution must be securely sandboxed.
Best practices:
eval(), Function(), exec(), spawn(), fork(), or similar APIs that interpret or execute code provided by users or external sourceschild_process APIs (e.g., spawn, exec, fork, execFile) or equivalent APIs to launch new processes, run shell commands, or invoke system binariesExample: Avoiding dangerous code execution
1 2// Never do this - executes arbitrary code const userInput = req.body.code; eval(userInput); // Never do this - spawns external processes const { exec } = require('child_process'); exec(userInput, (error, stdout, stderr) => { });
Allowing apps to spawn new processes or execute arbitrary code, especially code derived from user input introduces significant security risks, including remote code execution that compromises the app's runtime environment.
Explicitly list all egress domains in your Forge manifest. Do not use wildcard entries.
Implementation:
1 2# Correct: Explicitly list all egress domains permissions: external: fetch: backend: - 'https://api.example.com' - 'https://secure.example.com'
What to avoid:
1 2# Never do this - wildcard is prohibited permissions: external: fetch: backend: - '*' # Wildcard is prohibited - '*.com' # Wildcard patterns are also prohibited
It is critical to tightly control which external domains your app can communicate with. The Forge manifest file (manifest.yml) defines the set of domains your app is permitted to access for outbound (egress) network requests. Allowing unrestricted or overly broad network access such as by specifying a wildcard (*) for egress domains poses significant security risks, including the potential for data exfiltration, command-and-control attacks, or abuse of your app as a proxy for malicious activity.
Do not use unsafe-inline or unsafe-eval in your CSP directives when possible. These weaken your app's security posture and can lead to injection attacks.
Best practices:
<sha-algorithm>-<base64-value>) sources for inline scriptseval() and similar dynamic code executionUnderstanding CSP risks:
unsafe-inline allows browsers to execute any inline JavaScript code (such as <script>...</script> blocks or onclick attributes) directly in the HTML. This makes it much easier for attackers to inject malicious scripts if they can control any part of your custom UI implementation.unsafe-eval allows the use of JavaScript functions like eval(), new Function(), or setTimeout(), which can execute arbitrary code. Attackers can exploit this to run harmful scripts if they find a way to inject code.Example: Insecure CSP configuration
1 2# Incorrect: Using unsafe directives app: id: ari:cloud:ecosystem::app/your-app-id runtime: name: nodejsx.x permissions: content: scripts: - "unsafe-inline" - "unsafe-eval"
Secure coding is a foundational requirement for building trustworthy Forge apps, especially for Atlassian Government Cloud (AGC) compatibility. Developers should follow Atlassian's XSS prevention guidelines and understand OWASP Top 10 issues to prevent common vulnerabilities and ensure robust protection of customer data.
Forge Custom UI apps:
Forge Custom UI apps allow you to build custom interfaces using HTML, CSS, and JavaScript. These apps require additional security considerations:
Best practices:
innerText instead of innerHTML)Example: Input validation and sanitization
If you're building a Custom UI app and need to handle user-defined HTML content, use DOMPurify:
1 2import DOMPurify from 'dompurify'; // Correct: Validate and sanitize user input function processUserInput(userInput) { // Validate input type and length if (typeof userInput !== 'string' || userInput.length > 100) { throw new Error('Invalid input'); } // Sanitize HTML content using DOMPurify const sanitized = DOMPurify.sanitize(userInput, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'], ALLOWED_ATTR: [] }); return sanitized; } // Incorrect: Directly using user input function badProcessUserInput(userInput) { // XSS vulnerability document.getElementById('output').innerHTML = userInput; } // Correct: Use safe DOM methods function safeProcessUserInput(userInput) { // innerText automatically escapes HTML document.getElementById('output').innerText = userInput; }
If you use DOMPurify or other HTML sanitization libraries, you must regularly patch them. Browsers change functionality and bypasses are discovered regularly. See Atlassian's XSS prevention guide for more information.
All outbound connections must use HTTPS with TLS 1.2 or higher.
Best practices:
For apps that egress data, implement strong encryption at rest.
Best practices:
Using Forge encrypted storage:
For sensitive information such as API keys, passwords, tokens, or PII, use the encrypted storage API from @forge/kvs:
1 2import { kvs } from '@forge/kvs'; // Correct: Store secrets using Forge encrypted storage await kvs.setSecret('third-party-api-key', apiKey); // Retrieve encrypted secret const apiKey = await kvs.getSecret('third-party-api-key'); // Incorrect: Never hardcode secrets const apiKey = 'sk_live_1234567890abcdef'; // Never do this // Incorrect: Using regular storage for secrets import { kvs } from '@forge/kvs'; await kvs.set('third-party-api-key', apiKey); // Not encrypted!
Request only the minimum scopes and permissions necessary for your app's functionality.
Best practices:
Clearly disclose all third-party integrations and data handling practices.
Required disclosures:
This information must be made publicly available to customers via the Privacy & Security tab of your Marketplace listing. This transparency is critical for maintaining customer trust and aligning with FedRAMP and Atlassian Government Cloud (AGC) requirements.
Proper management of third-party dependencies is critical for maintaining the security posture of AGC-compatible apps and aligns with FedRAMP controls.
Best practices:
package.json and package-lock.json (or equivalent) files for outdated or insecure packages and Keep dependencies up to date with security patchesExample: Dependency scanning
1 2# Correct: Regularly audit dependencies npm audit # Fix vulnerabilities automatically where possible npm audit fix
Open source tools:
Address vulnerabilities according to severity-based timelines as per Security Bug Fix policy:
Implement regular automated security scanning. This aligns with FedRAMP's Configuration Management and Vulnerability Management control families, which emphasize continuous identification and mitigation of security weaknesses.
Required scanning:
Integration with CI/CD:
Integrate automated vulnerability scanning tools into your CI/CD pipeline to regularly assess your app's source code and dependencies for security weaknesses. This should include:
If you discover a security incident:
Establish and maintain secure configuration baselines for the Forge app, including remote resources and its dependencies, by maintaining an inventory such as a Software Bill of Materials (SBOM). Generate an SBOM automatically during each build or release using tools like cyclonedx-npm or npm, and update it whenever dependencies change to keep an accurate inventory of all components and their versions. Store and version control the SBOM files alongside your code for traceability and security auditing purposes. Regularly review and update these baselines to reflect changes in app functionality or dependencies.
1 2# Generate SBOM using npm (run from project root) npm sbom > sbom.json # Using CycloneDX format (run from project root) npx @cyclonedx/cyclonedx-npm --output-file sbom.json # Integrate into CI/CD # Add to your bitbucket-pipelines.yml or CI workflow
Best practices:
kvs.setSecret()), not the regular storage API (storage.set())Conduct regular security testing (manual and automated) to identify low hanging vulnerabilities.
Best practices:
Ensure that you complete all Partner verification steps as outlined in the Marketplace onboarding requirements. This process is essential for confirming your organization's identity and establishing trust with Atlassian and its customers.
Partner verification typically involves validating your business entity and verifying contact information to validate your identity.
Designate at least one security contact who:
An admin can also be listed as a security contact. See the vulnerability review practices guide for more information. Once your account is created, add your email address as a security contact by following the instructions in this link.
Key considerations
Rate this page: