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.
nest-aws-sdk
Advanced tools
A thin wrapping layer around the aws-sdk package for clean NestJS dependency injection.
npm install --save nest-aws-sdk aws-sdk
Below is an example of injecting the AwsSdkModule at the global root level. This also demonstrates some of the testing tools provided to make mocking and spying on AWS clients easier.
// app.module.ts
import { Module } from '@nestjs/common';
import { AwsSdkModule } from 'nest-aws-sdk';
import { SharedIniFileCredentials, S3 } from 'aws-sdk';
import { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
import { S3ManagerModule } from './s3-manager/s3-manager.module';
@Module({
imports: [
S3ManagerModule,
AwsSdkModule.forRoot({
defaultServiceOptions: {
region: 'us-east-1',
credentials: new SharedIniFileCredentials({
profile: 'my-profile',
}),
},
services: [S3],
}),
],
providers: [],
exports: [],
})
export class AppModule {}
// s3-manager.module.ts
import { Module } from '@nestjs/common';
import { S3ManagerService } from './s3-manager.service';
@Module({
imports: [],
providers: [S3ManagerService],
exports: [S3ManagerService],
})
class S3ManagerModule {}
// s3-manager.service.ts
import { Injectable } from '@nestjs/common';
import { InjectAwsService } from 'nest-aws-sdk';
import { S3 } from 'aws-sdk';
@Injectable()
export class S3ManagerService {
constructor(@InjectAwsService(S3) private readonly s3: S3) {}
async listBucketContents(bucket: string) {
const response = await this.s3.listObjectsV2({ Bucket: bucket }).promise();
return response.Contents.map(c => c.Key);
}
}
// s3-manager.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { S3 } from 'aws-sdk';
import {
createAwsServiceMock,
createAwsServicePromisableSpy,
getAwsServiceMock,
} from 'nest-aws-sdk/dist/testing';
import { S3ManagerService } from './s3-manager.service';
describe('S3ManagerService', () => {
describe('listBucketContents()', () => {
it('should call the list method and return the Content keys', async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
S3ManagerService,
createAwsServiceMock(S3, {
useValue: {
listObjectsV2: () => null,
},
}),
],
}).compile();
const service = module.get(S3ManagerService);
const listSpy = createAwsServicePromisableSpy(
getAwsServiceMock(module, S3),
'listObjectsV2',
'resolve',
{
Contents: [{ Key: 'myKey' }],
},
);
const result = await service.listBucketContents('myBucket');
expect(result.length).toBe(1);
expect(result[0]).toBe('myKey');
expect(listSpy).toHaveBeenCalledTimes(1);
expect(listSpy).toHaveBeenCalledWith({ Bucket: 'myBucket' });
});
});
});
Below is an example of injecting the AwsSdkModule global providers at the root-level and the client feature registration at the feature-submodule.
// app.module.ts
import { Module } from '@nestjs/common';
import { AwsSdkModule } from 'nest-aws-sdk';
import { SharedIniFileCredentials } from 'aws-sdk';
import { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
import { S3ManagerModule } from './s3-manager/s3-manager.module';
@Module({
imports: [
S3ManagerModule,
AwsSdkModule.forRootAsync({
defaultServiceOptions: {
useValue: {
region: 'us-east-1',
credentials: new SharedIniFileCredentials({
profile: 'my-profile',
}),
},
},
}),
],
providers: [],
exports: [],
})
export class AppModule {}
// s3-manager.module.ts
import { Module } from '@nestjs/common';
import { AwsSdkModule } from 'nest-aws-sdk';
import { S3 } from 'aws-sdk';
import { S3ManagerService } from './s3-manager.service';
@Module({
imports: [AwsSdkModule.forFeatures([S3])],
providers: [S3ManagerService],
exports: [S3ManagerService],
})
class S3ManagerModule {}
AwsSdkModule.forRoot()
is the simplest form of registration and uses statically assigned options
values.
defaultServiceOptions
is an optional object or object-returning method to get the aws-sdk ServiceConfigurationOptions
object. This includes the region
, credentials
, and other client-level configuration.
services
can optionally be registered at the root level by passing an array of aws-sdk types, i.e. S3
, or a AwsServiceWithServiceOptions
object. These are interchangable and can be used as such:
import { AwsSdkModule } from 'nest-aws-sdk';
import { CloudFront, S3, SharedIniFileCredentials } from 'aws-sdk';
@Module({
imports: [
AwsSdkModule.forRoot({
services: [
S3,
{
service: CloudFront,
serviceOptions: {
credentials: new SharedIniFileCredentials({
profile: 'aws-nest-sdk',
}),
},
},
],
}),
],
})
class AppRootModule {}
Note: the supplied values in serviceOptions will override the values supplied in the defaultServiceOptions object.
AwsSdkModule.forRootAsync()
is an injectable form of the AwsSdkModule.forRoot()
import and currently supports two types of instantiation: 'useFactory' and 'useValue'. Support for ClassProvider and ExistingProvider coming soon.
AwsSdkModule.forRootAsync()
can be called with no provided parameters. This will allow the AWS clients to be created without any provided credential context, which is not uncommon when running in an AWS environment.
// app.module.ts
import { Module } from '@nestjs/common';
import { AwsSdkModule } from 'nest-aws-sdk';
import { S3ManagerModule } from './s3-manager/s3-manager.module';
@Module({
imports: [S3ManagerModule, AwsSdkModule.forRootAsync()],
providers: [],
exports: [],
})
export class AppModule {}
useValue is the simplest way to modify the service options of the created clients.
// app.module.ts
import { Module } from '@nestjs/common';
import { AwsSdkModule } from 'nest-aws-sdk';
import { SharedIniFileCredentials } from 'aws-sdk';
import { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
import { S3ManagerModule } from './s3-manager/s3-manager.module';
@Module({
imports: [
S3ManagerModule,
AwsSdkModule.forRootAsync({
defaultServiceOptions: {
useValue: {
region: 'us-east-1',
credentials: new SharedIniFileCredentials({
profile: 'my-profile',
}),
},
},
}),
],
providers: [],
exports: [],
})
export class AppModule {}
useFactory allows for dynamic modification of the service options. This includes support for imports
and inject
.
// app.module.ts
import { Module } from '@nestjs/common';
import { AwsSdkModule } from 'nest-aws-sdk';
import { ConfigService, ConfigModule } from './config';
@Module({
imports: [
AwsSdkModule.forRootAsync({
defaultServiceOptions: {
useFactory: (cs: ConfigService) => {
return {
region: 'us-east-1',
credentials: cs.getCredentials(),
};
},
imports: [ConfigModule],
inject: [ConfigService],
},
}),
],
providers: [],
exports: [],
})
AwsSdkModule.forFeatures()
creates the providers for the AWS clients you wish to use at a module-specific level.
Note: forFeatures cannot be used in combination with root-level service registrations.
To provide clients to the module context, pass the client constructor symbol to the AwsSdkModule.forFeatures()
method. Note, it is best to import the client directly from aws-sdk
instead of from deeper paths - the deeper paths may produce unexpected behaviors.
import { Module } from '@nestjs/common';
import { S3 } from 'aws-sdk';
import { AwsSdkModule } from 'nest-aws-sdk';
@Module({
imports: [AwsSdkModule.forFeatures([S3])],
providers: [],
exports: [],
})
class AppSubModule {}
The AwsServiceFactory
class is exposed to the root- and feature-level. This allows for dynamic creation of AWS clients without feature registration. In addition, the default options are injectable via the @InjectAwsDefaultOptions()
decorator.
import { Injectable } from '@nestjs/common';
import { InjectAwsDefaultOptions } from '../src';
import { S3, SharedIniFileCredentials } from 'aws-sdk';
import { ServiceConfigurationOptions } from 'aws-sdk/lib/service';
@Injectable()
export class AppService {
constructor(
@InjectAwsDefaultOptions() readonly options: ServiceConfigurationOptions,
readonly factory: AwsServiceFactory,
) {}
}
For testing AWS clients, we recommend fully replacing the AWS clients as they have side-effects inside of the constructor. To do so, methods are made available to make mocking these clients very simple.
This method allows for override an AWS client instantiation. To mock a client, call the createAwsServiceMock
method with the client constructor and pass a provider object that will be used in its stead.
const module: TestingModule = await Test.createTestingModule({
providers: [
S3ManagerService,
createAwsServiceMock(S3, {
useValue: {
listObjectsV2: () => null,
},
}),
],
}).compile();
To retrieve a mock from the test bed via the correct symbol, getAwsServiceMock
is exported.
const s3 = getAwsServiceMock(module, S3);
It is common to want to use the .promise()
method returned by most AWS client methods. To make spying on these simpler, the createAwsServicePromisableSpy
creates a spy and returns a promised value.
it('should call the list method and return the Content keys', async () => {
const listSpy: jest.SpyInstance = createAwsServicePromisableSpy(
s3, // the mocked object to spy on
'listObjectsV2', // the method to spy on
'resolve', // 'resolve' or 'reject'
{ Contents: [{ Key: 'myKey' }] }, // the value to resolve or reject
);
const result = await service.listBucketContents('myBucket');
expect(result.length).toBe(1);
expect(result[0]).toBe('myKey');
expect(listSpy).toHaveBeenCalledTimes(1);
expect(listSpy).toHaveBeenCalledWith({ Bucket: 'myBucket' });
});
nest-aws-sdk is MIT licensed.
Nest-AWS-SDK is released through semantic-release. Effectively this means that versioning is based off commit messages. Please review angular-changelog-convention and commit under that format. Otherwise semantic-release won't pick up commits for versioning this library.
FAQs
A thin wrapping layer around the aws-sdk package for clean NestJS dependency injection.
The npm package nest-aws-sdk receives a total of 9,724 weekly downloads. As such, nest-aws-sdk popularity was classified as popular.
We found that nest-aws-sdk 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.