Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pangeacyber",
"version": "1.0.0",
"main": "index.js",
"private": true,
"repository": "https://github.com/pangeacyber/pangea-javascript.git",
"author": "Pangea (https://pangea.cloud)",
"license": "MIT",
Expand Down
10 changes: 7 additions & 3 deletions packages/pangea-node-sdk/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
"node": true
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["prettier", "@gitlab"],
"parserOptions": {
"project": true
},
"plugins": ["@typescript-eslint", "node"],
"extends": ["prettier", "plugin:@gitlab/jest"],
"ignorePatterns": ["/coverage/**", "/dev/**", "/dist/**"],
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double"],
"quotes": ["error", "double", { "avoidEscape": true }],
"jest/no-restricted-matchers": "off",
"node/no-unsupported-features/es-syntax": [
"error",
{
Expand Down
28 changes: 16 additions & 12 deletions packages/pangea-node-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"repository": "git@github.com:pangeacyber/pangea-javascript.git",
"author": "Glenn Gallien <glenn.gallien@pangea.cloud>",
"license": "MIT",
"engines": {
"node": "18 || >=20"
},
"dependencies": {
"@node-rs/crc32": "^1.7.2",
"crypto-js": "^4.2.0",
Expand All @@ -18,29 +21,30 @@
"merkle-tools": "^1.4.1"
},
"devDependencies": {
"@babel/eslint-parser": "^7.22.5",
"@gitlab/eslint-plugin": "^19.0.0",
"@gitlab/eslint-plugin": "^19.3.0",
"@types/crypto-js": "^4.2.0",
"@types/jest": "29.5.3",
"@typescript-eslint/eslint-plugin": "^5.35.1",
"@typescript-eslint/parser": "^5.35.1",
"@typescript-eslint/eslint-plugin": "^6.15.0",
"@typescript-eslint/parser": "^6.15.0",
"coverage": "^0.4.1",
"eslint": "^8.23.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.1.0",
"jest": "29.6.2",
"prettier": "^2.6.0",
"prettier": "^3.1.1",
"ts-jest": "^29.1.0",
"ts-node": "^9.1.1",
"tsc-alias": "^1.8.6",
"typedoc": "^0.24.8",
"typescript": "^5.1.6"
},
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
"test:unit": "NODE_OPTIONS='--experimental-vm-modules --openssl-legacy-provider' npx jest --testPathPattern=tests/unit",
"test:integration": "NODE_OPTIONS='--experimental-vm-modules --openssl-legacy-provider' npx jest --testPathPattern=tests/integration",
"lint": "npx prettier --check . && npx eslint --ext .ts",
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules npx jest",
"test:unit": "cross-env NODE_OPTIONS=\"--experimental-vm-modules --openssl-legacy-provider\" npx jest --testPathPattern=tests/unit",
"test:integration": "cross-env NODE_OPTIONS=\"--experimental-vm-modules --openssl-legacy-provider\" npx jest --testPathPattern=tests/integration",
"lint": "npx prettier --check . && npx eslint --ext .ts .",
"prettier:fix": "npx prettier . -w",
"generate:docs": "typedoc --tsconfig ./tsconfig.prod.json",
"build": "rm -fr dist/* && tsc -p tsconfig.prod.json && tsc-alias -p tsconfig.prod.json"
Expand Down
1 change: 0 additions & 1 deletion packages/pangea-node-sdk/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export namespace PangeaErrors {
constructor(message: string, response: PangeaResponse<any>) {
super(message);
this.name = "PangeanAPIError";
response.result = response.result as Errors;
this.response = response;
}

Expand Down
23 changes: 16 additions & 7 deletions packages/pangea-node-sdk/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,24 @@ class PangeaRequest {
}
}

async post(
/**
* `POST` request.
*
* @template R Result type.
* @param endpoint Endpoint path.
* @param data Request body.
* @param options Additional options.
* @returns A `Promise` of the response.
*/
async post<R>(
endpoint: string,
data: Request,
options: PostOptions = {}
): Promise<PangeaResponse<any>> {
): Promise<PangeaResponse<R>> {
const url = this.getUrl(endpoint);
this.checkConfigID(data);

let response;
let response: Response;
let entry = options.files ? Object.entries(options.files)[0] : undefined;
if (options.files && entry) {
if (data.transfer_method === TransferMethod.POST_URL) {
Expand Down Expand Up @@ -272,12 +281,12 @@ class PangeaRequest {
}
}

private async handleHttpResponse(
private async handleHttpResponse<R>(
response: Response,
options: PostOptions = {}
): Promise<PangeaResponse<any>> {
): Promise<PangeaResponse<R>> {
try {
let pangeaResponse = new PangeaResponse(response);
let pangeaResponse = new PangeaResponse<R>(response);
if (response.statusCode === 202) {
if (options.pollResultSync !== false) {
pangeaResponse = await this.handleAsync(pangeaResponse);
Expand Down Expand Up @@ -407,7 +416,7 @@ class PangeaRequest {
return headers;
}

private checkResponse(response: PangeaResponse<any>) {
private checkResponse<T>(response: PangeaResponse<T>): PangeaResponse<T> {
if (response.success) {
return response;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/pangea-node-sdk/src/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class PangeaResponse<M> extends ResponseObject<M> {
constructor(response: Response) {
const obj = JSON.parse(JSON.stringify(response.body), parseJSONfields);
super(obj);
this.gotResponse = response as Response;
this.gotResponse = response;
this.success = this.status === "Success";
this.result = this.result == null ? ({} as M) : this.result;
if (this.gotResponse.statusCode == 202) {
Expand Down
13 changes: 11 additions & 2 deletions packages/pangea-node-sdk/src/services/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,20 @@ class BaseService {
return await this.request.get(endpoint);
}

async post(
/**
* `POST` request.
*
* @template R Result type.
* @param endpoint Endpoint path.
* @param data Request body.
* @param options Additional options.
* @returns A `Promise` of the response.
*/
async post<R>(
endpoint: string,
data: object,
options: PostOptions = {}
): Promise<PangeaResponse<any>> {
): Promise<PangeaResponse<R>> {
return await this.request.post(endpoint, data, options);
}

Expand Down
45 changes: 43 additions & 2 deletions packages/pangea-node-sdk/src/services/vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class VaultService extends BaseService {
* @summary List
* @description Look up a list of secrets, keys and folders, and their associated information.
* @operationId vault_post_v1_list
* @param {Object} options - The following options are supported:
* @param {Vault.ListOptions} options - The following options are supported:
* - filter (object): A set of filters to help you customize your search. Examples:
* `"folder": "/tmp"`, `"tags": "personal"`, `"name__contains": "xxx"`, `"created_at__gt": "2020-02-05T10:00:00Z"`
* For metadata, use: `"metadata_": "<value>"`
Expand Down Expand Up @@ -698,7 +698,6 @@ class VaultService extends BaseService {
* "pvi_p6g5i3gtbvqvc3u6zugab6qs6r63tqf5",
* "lJkk0gCLux+Q+rPNqLPEYw=="
* "FfWuT2Mq/+cxa7wIugfhzi7ktZxVf926idJNgBDCysF/knY9B7M6wxqHMMPDEBs86D8OsEGuED21y3J7IGOpCQ==",
* 1
* );
* ```
*/
Expand Down Expand Up @@ -806,6 +805,48 @@ class VaultService extends BaseService {
): Promise<PangeaResponse<Vault.Folder.CreateResult>> {
return this.post("v1/folder/create", request);
}

/**
* @summary Encrypt structured
* @description Encrypt parts of a JSON object.
* @operationId vault_post_v1_key_encrypt_structured
* @param request Request parameters.
* @returns A `Promise` of the encrypted result.
* @example
* ```js
* const response = await vault.encryptStructured({
* id: "pvi_[...]",
* structured_data: {"field1": [1, 2, "true", "false"], "field2": "data2"},
* filter: "$.field1[2:4]",
* });
* ```
*/
async encryptStructured<O>(
request: Vault.Key.EncryptStructuredRequest<O>
): Promise<PangeaResponse<Vault.Key.EncryptStructuredResult<O>>> {
return this.post("v1/key/encrypt/structured", request);
}

/**
* @summary Decrypt structured
* @description Decrypt parts of a JSON object.
* @operationId vault_post_v1_key_decrypt_structured
* @param request Request parameters.
* @returns A `Promise` of the decrypted result.
* @example
* ```js
* const response = await vault.decryptStructured({
* id: "pvi_[...]",
* structured_data: {"field1": [1, 2, "[...]", "[...]"], "field2": "data2"},
* filter: "$.field1[2:4]",
* });
* ```
*/
async decryptStructured<O>(
request: Vault.Key.EncryptStructuredRequest<O>
): Promise<PangeaResponse<Vault.Key.EncryptStructuredResult<O>>> {
return this.post("v1/key/decrypt/structured", request);
}
}

export default VaultService;
57 changes: 57 additions & 0 deletions packages/pangea-node-sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,63 @@ export namespace Vault {
purpose: string;
public_key?: EncodedPublicKey;
}

/**
* Parameters for an encrypt/decrypt structured request.
*/
export interface EncryptStructuredRequest<O = object> {
/**
* The ID of the key to use. It must be an item of type `symmetric_key` or
* `asymmetric_key` and purpose `encryption`.
*/
id: string;

/**
* Structured data for applying bulk operations.
*/
structured_data: O;

/**
* A filter expression. It must point to string elements of the
* `structured_data` field.
*/
filter: string;

/**
* The item version. Defaults to the current version.
*/
version?: number;

/**
* User provided authentication data.
*/
additional_data?: string;
}

/**
* Result of an encrypt/decrypt structured request.
*/
export interface EncryptStructuredResult<O = object> {
/**
* The ID of the item.
*/
id: string;

/**
* The item version.
*/
version: number;

/**
* The algorithm of the key.
*/
algorithm: string;

/**
* Structured data with filtered fields encrypted.
*/
structured_data: O;
}
}

export namespace Asymmetric {
Expand Down
2 changes: 1 addition & 1 deletion packages/pangea-node-sdk/tests/integration/audit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ it("log multi config 2. no verbose", async () => {
expect(response.result.signature_verification).toBe("none");
});

it("log multi config token, without config id ", async () => {
it("log multi config token, without config id", async () => {
const config = new PangeaConfig({ domain: domain, customUserAgent: "sdk-test" });
const audit = new AuditService(tokenMultiConfig, config);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ it("file filepathReputationBulk with filepath should succeed", async () => {
expect(Object.keys(response.result.data).length).toBe(2);
});

it("file reputation with filepath should faild", async () => {
it("file reputation with filepath should fail", async () => {
const options = { provider: "reversinglabs", verbose: true, raw: true };

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ it("redact multi config 2. no verbose", async () => {
expect(response.result).toEqual(expected);
});

it("log multi config token, without config id ", async () => {
it("log multi config token, without config id", async () => {
const config = new PangeaConfig({
domain: domain,
customUserAgent: "sdk-test",
Expand Down
32 changes: 31 additions & 1 deletion packages/pangea-node-sdk/tests/integration/vault.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ it("AES encrypting life cycle", async () => {
const algorithm = Vault.SymmetricAlgorithm.AES;
const purpose = Vault.KeyPurpose.ENCRYPTION;
try {
const id = await symGenerateDefault(algorithm as Vault.SymmetricAlgorithm, purpose);
const id = await symGenerateDefault(algorithm, purpose);
await encryptingCycle(id);
await vault.delete(id);
} catch (e) {
Expand Down Expand Up @@ -573,3 +573,33 @@ it("Folder endpoint", async () => {
const deleteParentResp = await vault.delete(createParentResp.result.id);
expect(createParentResp.result.id).toBe(deleteParentResp.result.id);
});

it("encrypt structured", async () => {
const key = await symGenerateDefault(
Vault.SymmetricAlgorithm.AES256_CFB,
Vault.KeyPurpose.ENCRYPTION
);
const data = { field1: [1, 2, "true", "false"], field2: "data2" };

// Encrypt.
const encrypted = await vault.encryptStructured({
id: key,
structured_data: data,
filter: "$.field1[2:4]",
});
expect(encrypted.result.id).toStrictEqual(key);
const encryptedData = encrypted.result.structured_data;
expect(encryptedData.field1).toHaveLength(data.field1.length);
expect(encryptedData.field2).toStrictEqual(data.field2);

// Decrypt.
const decrypted = await vault.decryptStructured({
id: key,
structured_data: encryptedData,
filter: "$.field1[2:4]",
});
expect(decrypted.result.id).toStrictEqual(key);
const decryptedData = decrypted.result.structured_data;
expect(decryptedData.field1).toStrictEqual(data.field1);
expect(decryptedData.field2).toStrictEqual(data.field2);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Signer file does not exist 1`] = `"ENOENT: no such file or directory, open './this/is/not/a/file'"`;
Loading