Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
@forge/util
Advanced tools
The official TypeScript library for creating, parsing and working with ARIs 🚀
The classes in this repository are auto-generated based upon the ARI Registry. We plan to release a new version of this library at the start of every business week, AEST.
To install this library, run:
yarn add @atlassian/ari
or
npm i @atlassian/ari
This library aims to give you the primitives you need to get working with ARIs. To the extent we can, we aim to provide a lightweight API for our consumers.
You can import a specific ARI class from a few places, depending on your bundle size requirements:
// Import from an entry point specific to a resource owner (Confluence).
import { ConfluencePageAri } from '@atlassian/ari/confluence';
// Import from an entry point specific to a resource owner (Confluence) and a resource type (page).
import { ConfluencePageAri } from '@atlassian/ari/confluence/page';
// Import core classes from root entry point.
import { Ari, AnyAri, Ati } from '@atlassian/ari';
Most of the methods and classes you can interact with are shown below. For a more detailed reference, keep scrolling until the API reference section.
// Import core classes from root entrypoint.
import { AnyAri, Ati, ResourceOwner, ResourceType } from '@atlassian/ari';
// Import from an entry-point specific to a resource owner (Confluence) and a resource type (page).
import { ConfluenceSiteAri } from '@atlassian/ari/confluence/site';
// ===========================================================================
// Option 1️⃣: work with ARI classes representing definitions from the registry.
// ===========================================================================
// - Create an ARI, using segments defined in the registry definition.
const pageAri = ConfluencePageAri.create({
siteId: '123',
pageId: '456',
});
// - Access specific segments for this ARI.
console.log(pageAri.siteId); // yields '123'
console.log(pageAri.pageId); // yields '456'
// - Access generic segments conforming to the ARI specification.
console.log(pageAri.cloudId); // yields '123'
console.log(pageAri.resourceOwner); // yields 'confluence'
console.log(pageAri.resourceType); // yields 'page'
console.log(pageAri.resourceId); // yields '456'
// - Access the ATI for this segment.
console.log(pageAri.ati); // yields Ati { resourceOwner: 'confluence', resourceType: 'page' }
// - Check if an ARI string (or class) can be interepted as this type.
console.log(ConfluenceSiteAri.check('ari:cloud:jira::site/ee95456c-a203-42dc-b284-7bbb3854457f')); // yields 'false'
console.log(ConfluenceSiteAri.check('ari:cloud:confluence::site/ee95456c-a203-42dc-b284-7bbb3854457f')); // yields 'true'
// ===========================================================================
// Option 2️⃣: work with generic ARI classes, conforming to the ARI specification.
// ===========================================================================
// - Create an ARI based on the specification (no convenience methods)
const pageAriTheSecond = AnyAri.create({
resourceOwner: ResourceOwner.Confluence,
cloudId: '123',
resourceType: ResourceType.Page,
resourceId: '456',
});
// - Access generic segments conforming to the ARI specification
console.log(pageAri.cloudId); // yields '123'
console.log(pageAri.resourceOwner); // yields 'confluence'
console.log(pageAri.resourceType); // yields 'page'
console.log(pageAri.resourceId); // yields '456'
// - Access the ATI for this segment
console.log(pageAri.ati); // yields Ati { resourceOwner: 'confluence', resourceType: 'page' }
Lastly, here is an example of parsing into a specific ARI type:
import { ConfluencePageAri, JiraIssueAri, TrelloCardAri } from '@atlassian/ari';
const invokeApi = (ari: string): Promise<Ari> => {
if (TrelloCardAri.check(ari)) {
return this.http.client.post(`/trello/api/${ari}`);
}
if (JiraIssueAri.check(ari)) {
return this.http.client.post(`/jira/api/${ari}`);
}
if (ConfluencePageAri.check(ari)) {
return this.http.client.post(`/confluence/api/${ari}`);
}
};
const ari = await invokeApi('ari:cloud:confluence:123:page/456'); // demonstrative of a valid Confluence Page ARI
console.log(ari.resourceId); // yields the resource ID returned from the upstream API
If you are parsing an ARI from the registry, you can use a specific Ari
class to parse from string
to that instance (e.g. ConfluencePageAri
), or the generic Ari
for unregistered ARIs.
// Import from an entry-point specific to a resource owner (Confluence) and a resource type (page).
import { ConfluencePageAri } from '@atlassian/ari/confluence/page';
const ari = ConfluencePageAri.parse('ari:cloud:confluence:123:page/456'); // yields a ConfluencePageAri class instance
console.log(ari.siteId); // yields 123
console.log(ari.pageId); // yields 456
If you are parsing an ARI which does not exist in the registry (:paddlin:) you can use the core ARI class.
// Import core classes from root entrypoint.
import { AnyAri } from '@atlassian/ari';
const ari = AnyAri.parse('ari:cloud:confluence:123:page/456'); // yields a generic ARI class instance
console.log(ari.cloudId); // yields 123
console.log(ari.resourceId); // yields 456
Note: if the invocation of .parse()
fails, a ValidationError
will be thrown.
// Import core classes from root entrypoint.
import { AnyAri } from '@atlassian/ari';
try {
const ari = AnyAri.parse('ari:cloud:confluence:123:unregistered-ari/456'); // yields a generic ARI class instance
} catch (err) {
if (err.constructor.name === 'ValidationError') {
// handle error
}
}
If you are creating an ARI from the registry, you can use a specific Ari
class to create from a set of options for that instance in conformance with its definition in the ARI registry (e.g. ConfluencePageAri
), or the generic Ari
for unregistered ARIs.
// Import from an entry-point specific to a resource owner (Confluence) and a resource type (page).
import { ConfluencePageAri } from '@atlassian/ari/confluence/page';
const ari = ConfluencePageAri.create({
// yields a ConfluencePageAri class instance
siteId: '123',
pageId: '456',
});
console.log(ari.siteId); // yields 123
console.log(ari.pageId); // yields 456
If you are creating an ARI which does not exist in the registry (double :paddlin:) you can use the core ARI class.
// Import core classes from root entrypoint.
import { AnyAri } from '@atlassian/ari';
const ari = AnyAri.create({
// yields a generic ARI class instance
resourceOwner: 'confluence',
cloudId: '123',
resourceType: 'page',
resourceId: '456',
});
console.log(ari.cloudId); // yields 123
console.log(ari.resourceId); // yields 456
Note: if the invocation of .create()
or parse()
fails, a ValidationError
will be thrown.
// Import core classes from root entrypoint.
import { AnyAri } from '@atlassian/ari';
try {
const ari = AnyAri.parse('ari:cloud:confluence:123:unregistered-ari/456'); // yields a generic ARI class instance
} catch (err) {
if (err.constructor.name === 'ValidationError') {
// handle error
}
}
You can use the check()
function to determine if an ARI string or class fits the requirements of a certain ARI.
import { AnyAri, ConfluenceSiteAri, JiraSiteAri } from '@atlassian/ari';
const result1 = AnyAri.check(ConfluenceSiteAri.create({ siteId: 'ee95456c-a203-42dc-b284-7bbb3854457f' }));
const result2 = JiraSiteAri.check('ari:cloud:confluence::site/ee95456c-a203-42dc-b284-7bbb3854457f');
console.log(result1); // yields true
console.log(result2); // yields false
For ATIs, the use of more specific instance is not required. The core Ati
class should be used:
// Import core classes from root entrypoint.
import { Ati, ResourceOwner, ResourceType } from '@atlassian/ari';
const ati = Ati.parse('ati:cloud:confluence:page');
console.log(ati.resourceOwner); // yields `confluence`
console.log(ati.resourceType); // yields `page`
For ATIs, the use of more specific instance is not required. The core Ati
class should be used:
// Import core classes from root entrypoint.
import { Ati, ResourceOwner, ResourceType } from '@atlassian/ari';
const ati = Ati.create({
resourceOwner: ResourceOwner.Confluence,
resourceType: ResourceType.Page,
});
console.log(ati.toString()); // yields `ati:cloud:confluence:page`
ARMs are exposed through the top-level Arm
class. To parse an ARM string do the following:
// Import `Arm` class from root entrypoint.
import { Arm } from '@atlassian/ari';
const arm = Arm.parse('arm:cloud:jira.*::board/.+');
console.log(arm.resourceOwnerMatcher); // yields `jira.*`
console.log(arm.resourceTypeMatcher); // yields `board`
You can create an ARM using the same Arm
class mentioned above!
// Import `Arm` from root entrypoint.
import { Arm } from '@atlassian/ari';
const arm = Arm.create({
resourceOwnerMatcher: 'jira.*',
cloudIdMatcher: '',
resourceTypeMatcher: 'board',
resourceIdMatcher: '.+',
});
console.log(arm.toString()); // yields `arm:cloud:jira.*::board/.+`
You can match ARIs against ARMs using the .match
method on ARM instances:
// Import `Arm` from root entrypoint.
import { Arm } from '@atlassian/ari';
import { ConfluencePageAri } from '@atlassian/ari/confluence/page';
const ari = ConfluencePageAri.create({
siteId: '123',
pageId: '456',
});
const arm = Arm.create({
resourceOwnerMatcher: 'confluence',
cloudIdMatcher: '[0-9]+',
resourceTypeMatcher: 'page',
resourceIdMatcher: '.+',
});
console.log(arm.match(ari)); // yields `true`
I have a feature request - where should I raise it?
Please create a ticket on this Trello board. We will try to get back to you ASAP!
I cannot see an ARI which should exist in this repository. What do I do?
Try to figure out who the resource owner of the ARI is, and kindly ask them to add their ARI to the ARI Registry YAML 😄
We auto-generate classes based on what exists in the registry. Teams should be reflecting their ARI back into it, using the process defined here.
If your ARI has recently been added to the registry, you will have to wait until the start of the next business week (AEST) for the changes to be reflected in this library.
I need a change to go through urgently. Who do I speak to?
Come and chat to us in the #ari-working-group channel!
If you're keen on helping us build out this library, reach out to us in #ari-working-group 😄
As a Working Group, we meet every fortnight. Library developers and maintainers will be present - we'd love to meet you and collaborate with you on this!
FAQs
Unknown package
The npm package @forge/util receives a total of 10,919 weekly downloads. As such, @forge/util popularity was classified as popular.
We found that @forge/util demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.