Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
aws-core-utils
Advanced tools
Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Lambdas, AWS errors, stream events, Kinesis, DynamoDB.DocumentClients, etc.
Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Lambdas, AWS errors, stream events, Kinesis, DynamoDB.DocumentClients, etc.
Currently includes:
handler
functions for and for working with AWS Lambdas that are exposed via API Gateway
aws-core-utils/other-lambdas
modulehandler
functions for and for working with "other" AWS Lambdas that are NOT exposed via
API Gateway and IDEALLY NOT triggered by a Kinesis or DynamoDB stream event source mapping
aws-core-utils/api-lambdas
modulekinesis-stream-consumer
moduledynamodb-stream-consumer
moduleThis module is exported as a Node.js module.
Using npm:
$ {sudo -H} npm i -g npm
$ npm i --save aws-core-utils
In Node.js:
api-lambdas
module within your API Gateway exposed Lambda:const apiLambdas = require('aws-core-utils/api-lambdas');
const isInstanceOf = require('core-functions/objects').isInstanceOf;
const appErrors = require('core-functions/app-errors');
const BadRequest = appErrors.BadRequest;
const createContext = () => ({}); // or your own pre-configured context
// To configure your handler to use Lambda Proxy integration and custom default response headers
const createOptions = () => require('./test/api-lambdas-context-options-2.json'); // with whatever options you want to use to configure your handler, stage handling, logging, custom settings, ...
const createSettings = () => undefined; // or whatever settings object you want to use to configure your handler, stage handling, logging, custom settings, ...
function exampleFunction(event, context) { /* ... */ } // implement and name your own function that does the actual work
const opts = {
logRequestResponseAtLogLevel: 'INFO',
invalidRequestMsg: 'Invalid request ...',
failureMsg: 'Failed to ...',
successMsg: 'Finished ...'
};
// Simplest approach - generate your API Gateway exposed Lambda's handler function
exports.handler = apiLambdas.generateHandlerFunction(createContext, createSettings, createOptions, exampleFunction, opts);
// OR ... develop your own Lambda handler function (e.g. simplistic example below - see apiLamdas.generateHandlerFunction for a MUCH better version)
exports.handler = (event, awsContext, callback) => {
const opts = {
// logRequestResponseAtLogLevel: 'info',
// invalidRequestMsg: 'Invalid request ...',
// failureMsg: 'Failed to ...',
// successMsg: 'Finished ...'
};
// Configure a standard context
let context = {};
try {
context = apiLambdas.configureHandlerContext(createContext, createSettings, createOptions, event, awsContext);
// ... execute Lambda specific code passing the context to your functions as needed
exampleFunction(event, context)
.then(response => {
context.info(opts.successMsg || 'Finished ...');
apiLambdas.succeedLambdaCallback(callback, response, event, context);
})
.catch(err => {
// Fail your Lambda callback and map the error to one of the default set of HTTP status codes:
// i.e. [400, 401, 403, 404, 408, 429, 500, 502, 503, 504]
if (isInstanceOf(err, BadRequest) || appErrors.getHttpStatus(err) === 400) {
context.warn(opts.invalidRequestMsg || 'Invalid request ...', err.message);
} else {
context.error(opts.failureMsg || 'Failed to ...', err);
}
apiLambdas.failLambdaCallback(callback, err, event, context);
});
} catch (err) {
// Fail your Lambda callback and map the error to one of the default set of HTTP status codes:
// i.e. [400, 401, 403, 404, 408, 429, 500, 502, 503, 504]
context.error('Failed to ...', err);
apiLambdas.failLambdaCallback(callback, err, event, context);
}
};
// ALTERNATIVE handler options for `succeedLambdaCallback` & `failLambdaCallback`:
// Fail your Lambda callback: using Lambda Proxy integration; using a custom response header; and map the error to one of a specified set of HTTP status codes
context.handler = { // simplistic example - should rather be set through an appropriate `context-options.json` file
useLambdaProxy: true,
defaultHeaders: {MyCustomHeader: 'MyCustomHeaderValue'},
allowedHttpStatusCodes: [400, 404, 418, 500, 508]
};
apiLambdas.failLambdaCallback(callback, new Error('Boom'), event, context);
const arns = require('aws-core-utils/arns');
const arnComponent = arns.getArnComponent(arn, index);
const arnPartition = arns.getArnPartition(arn);
const arnService = arns.getArnService(arn);
const arnRegion = arns.getArnRegion(arn);
const arnAccountId = arns.getArnAccountId(arn);
const arnResources = arns.getArnResources(arn);
assert(arnComponent && arnPartition && arnService && arnRegion && arnAccountId && arnResources);
const awsErrors = require('aws-core-utils/aws-errors');
assert(awsErrors);
contexts.js
module:// To use the configureStandardContext function, please refer to the 'To use the `api-lambdas` module' example above
// (since the `api-lambdas.js` module simply re-exports the `contexts.js` module's configureStandardContext)
// ALTERNATIVELY if you need the logic of the configureStandardContext function for custom purposes
const contexts = require('aws-core-utils/contexts');
const context = {};
const standardOptions = require('my-standard-options.json'); // or whatever options you want to use to configure stage handling, logging, custom settings, ...
const standardSettings = {}; // or whatever settings you want to use to configure stage handling, logging, custom settings, ...
contexts.configureStandardContext(context, standardSettings, standardOptions, awsEvent, awsContext);
// If you need the logic of the configureCustomSettings function, which is used by configureStandardContext, for other purposes
const myCustomSettings = {myCustomSetting1: 1, myCustomSetting2: 2, myCustomFunction: () => {}};
console.log(`Irrelevant logging - only added to avoid unused function warning - ${myCustomSettings.myCustomFunction()}`);
const myCustomOptions = require('my-custom-options.json');
contexts.configureCustomSettings(context, myCustomSettings, myCustomOptions);
console.log(`context.custom = ${JSON.stringify(context.custom)}; myCustomFunction = ${JSON.stringify(context.custom.myCustomFunction)} `);
const dynamoDBDocClientCache = require('aws-core-utils/dynamodb-doc-client-cache');
// Preamble to create a context and configure logging on the context
const context = {};
const logging = require('logging-utils');
logging.configureLogging(context); // or your own custom logging configuration (see logging-utils README.md)
// Define the DynamoDB.DocumentClient's constructor options that you want to use, e.g.
const dynamoDBDocClientOptions = {
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#constructor-property
// and http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#constructor-property
maxRetries: 0
// ...
};
// To create and cache a new AWS DynamoDB.DocumentClient instance with the given DynamoDB.DocumentClient constructor
// options for either the current region or the region specified in the given options OR reuse a previously cached
// DynamoDB.DocumentClient instance (if any) that is compatible with the given options
const dynamoDBDocClient = dynamoDBDocClientCache.setDynamoDBDocClient(dynamoDBDocClientOptions, context);
// To configure a new AWS.DynamoDB.DocumentClient instance (or re-use a cached instance) on a context
dynamoDBDocClientCache.configureDynamoDBDocClient(context, dynamoDBDocClientOptions);
console.log(context.dynamoDBDocClient);
// To get a previously set or configured AWS DynamoDB.DocumentClient instance for the current AWS region
const dynamoDBDocClient1 = dynamoDBDocClientCache.getDynamoDBDocClient();
// ... or for a specified region
const dynamoDBDocClient2 = dynamoDBDocClientCache.getDynamoDBDocClient('us-west-2');
// To get the original options that were used to construct a cached AWS DynamoDB.DocumentClient instance for the current or specified AWS region
const optionsUsed1 = dynamoDBDocClientCache.getDynamoDBDocClientOptionsUsed();
const optionsUsed2 = dynamoDBDocClientCache.getDynamoDBDocClientOptionsUsed('us-west-1');
// To delete and remove a cached DynamoDB.DocumentClient instance from the cache
const deleted = dynamoDBDocClientCache.deleteDynamoDBDocClient('eu-west-1');
assert(dynamoDBDocClient && dynamoDBDocClient1 && dynamoDBDocClient2 && optionsUsed1 && optionsUsed2 && deleted);
const kinesisCache = require('aws-core-utils/kinesis-cache');
// Preamble to create a context and configure logging on the context
const context = {};
const logging = require('logging-utils');
logging.configureLogging(context); // or your own custom logging configuration (see logging-utils README.md)
// Define the Kinesis constructor options that you want to use, e.g.
const kinesisOptions = {
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Kinesis.html#constructor-property for full details
maxRetries: 0
// ...
};
// To create and cache a new AWS Kinesis instance with the given Kinesis constructor options for either the current
// region or the region specified in the given options OR reuse a previously cached Kinesis instance (if any) that is
// compatible with the given options
const kinesis = kinesisCache.setKinesis(kinesisOptions, context);
// To configure a new AWS.Kinesis instance (or re-use a cached instance) on a context
kinesisCache.configureKinesis(context, kinesisOptions);
console.log(context.kinesis);
// To get a previously set or configured AWS Kinesis instance for the current AWS region
const kinesis1 = kinesisCache.getKinesis();
// ... or for a specified region
const kinesis2 = kinesisCache.getKinesis('us-west-2');
// To get the original options that were used to construct a cached AWS Kinesis instance for the current or specified AWS region
const optionsUsed1 = kinesisCache.getKinesisOptionsUsed();
const optionsUsed2 = kinesisCache.getKinesisOptionsUsed('us-west-1');
// To delete and remove a cached Kinesis instance from the cache
const deleted = kinesisCache.deleteKinesis('eu-west-1');
assert(kinesis && kinesis1 && kinesis2 && optionsUsed1 && optionsUsed2 && deleted);
const kmsCache = require('aws-core-utils/kms-cache');
// Preamble to create a context and configure logging on the context
const context = {};
const logging = require('logging-utils');
logging.configureLogging(context); // or your own custom logging configuration (see logging-utils README.md)
// Define the KMS constructor options that you want to use, e.g.
const kmsOptions = {
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/KMS.html#constructor-property for full details
maxRetries: 0
// ...
};
// To create and cache a new AWS KMS instance with the given KMS constructor options for either the current
// region or the region specified in the given options OR reuse a previously cached KMS instance (if any) that is
// compatible with the given options
const kms = kmsCache.setKMS(kmsOptions, context);
// To configure a new AWS.KMS instance (or re-use a cached instance) on a context
kmsCache.configureKMS(context, kmsOptions);
console.log(context.kms);
// To get a previously set or configured AWS KMS instance for the current AWS region
const kms1 = kmsCache.getKMS();
// ... or for a specified region
const kms2 = kmsCache.getKMS('us-west-2');
// To get the original options that were used to construct a cached AWS KMS instance for the current or specified AWS region
const optionsUsed1 = kmsCache.getKMSOptionsUsed();
const optionsUsed2 = kmsCache.getKMSOptionsUsed('us-west-1');
// To delete and remove a cached KMS instance from the cache
const deleted = kmsCache.deleteKMS('eu-west-1');
assert(kms && kms1 && kms2 && optionsUsed1 && optionsUsed2 && deleted);
const kmsUtils = require('aws-core-utils/kms-utils');
const kms = new AWS.KMS({region: 'eu-west-1'}); // or better yet use kms-cache module as above
// Preamble to create a context and configure logging on the context
const logging = require('logging-utils');
const logger = logging.configureLogging({}); // or your own custom logging configuration (see logging-utils README.md)
const accountId = 'XXXXXXXXXXXX'; // use your AWS account ID
const kmsKeyAlias = 'aws/lambda'; // or use your own key alias
const keyId = `arn:aws:kms:us-west-2:${accountId}:alias/${kmsKeyAlias}`;
// To encrypt plaintext using KMS:
const plaintext = 'Shhhhhhhhhhhhhhh'; // use your own plaintext
kmsUtils.encryptKey(kms, keyId, plaintext, logger)
.then(ciphertextBase64 => console.log(JSON.stringify(ciphertextBase64)));
// To decrypt ciphertext using KMS:
const ciphertextBase64 = '...'; // use your own ciphertext
kmsUtils.decryptKey(kms, ciphertextBase64, logger)
.then(plaintext => console.log(JSON.stringify(plaintext)));
// To encrypt plaintext using KMS:
const encryptParams = {KeyId: keyId, Plaintext: plaintext};
kmsUtils.encrypt(kms, encryptParams, logger)
.then(result => console.log(JSON.stringify(result)));
// To decrypt ciphertext using KMS:
const decryptParams = {CiphertextBlob: new Buffer(ciphertextBase64, 'base64')};
kmsUtils.decrypt(kms, decryptParams, logger)
.then(result => console.log(JSON.stringify(result)));
const lambdaCache = require('aws-core-utils/lambda-cache');
// Preamble to create a context and configure logging on the context
const context = {};
const logging = require('logging-utils');
logging.configureLogging(context); // or your own custom logging configuration (see logging-utils README.md)
// Define the Lambda constructor options that you want to use, e.g.
const lambdaOptions = {
// See http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#constructor-property for full details
maxRetries: 0
// ...
};
// To create and cache a new AWS Lambda instance with the given Lambda constructor options for either the current
// region or the region specified in the given options OR reuse a previously cached Lambda instance (if any) that is
// compatible with the given options
const lambda = lambdaCache.setLambda(lambdaOptions, context);
// To configure a new AWS.Lambda instance (or re-use a cached instance) on a context
lambdaCache.configureLambda(context, lambdaOptions);
console.log(context.lambda);
// To get a previously set or configured AWS Lambda instance for the current AWS region
const lambda1 = lambdaCache.getLambda();
// ... or for a specified region
const lambda2 = lambdaCache.getLambda('us-west-2');
// To get the original options that were used to construct a cached AWS Lambda instance for the current or specified AWS region
const optionsUsed1 = lambdaCache.getLambdaOptionsUsed();
const optionsUsed2 = lambdaCache.getLambdaOptionsUsed('us-west-1');
// To delete and remove a cached Lambda instance from the cache
const deleted = lambdaCache.deleteLambda('eu-west-1');
assert(lambda && lambda1 && lambda2 && optionsUsed1 && optionsUsed2 && deleted);
const lambdaUtils = require('aws-core-utils/lambda-utils');
const lambda = new AWS.Lambda({region: 'eu-west-1'}); // or better yet use lambda-cache module as above
// To list the event source mappings on your Lambda function
const params = {FunctionName: 'my-lambda-function'};
lambdaUtils.listEventSourceMappings(lambda, params, context)
.then(result => console.log(JSON.stringify(result)));
// To update an event source mapping on your Lambda function
const params2 = {FunctionName: 'my-lambda-function', UUID: uuid, BatchSize: 99};
lambdaUtils.updateEventSourceMapping(lambda, params2, context)
.then(result => console.log(JSON.stringify(result)));
// To disable an event source mapping on your Lambda function
lambdaUtils.disableEventSourceMapping(lambda, 'my-lambda-function', uuid, context)
.then(result => console.log(JSON.stringify(result)));
const lambdas = require('aws-core-utils/lambdas');
// To resolve the Lambda alias from an AWS Lambda context
const alias = lambdas.getAlias(awsContext);
// To extract other details from an AWS Lambda context
const functionName = lambdas.getFunctionName(awsContext);
const functionVersion = lambdas.getFunctionVersion(awsContext);
const functionNameVersionAndAlias = lambdas.getFunctionNameVersionAndAlias(awsContext);
const invokedFunctionArn = lambdas.getInvokedFunctionArn(awsContext);
const invokedFunctionArnFunctionName = lambdas.getInvokedFunctionArnFunctionName(awsContext);
assert(alias && functionName && functionVersion && functionNameVersionAndAlias && invokedFunctionArn && invokedFunctionArnFunctionName);
const regions = require('aws-core-utils/regions');
// To get the current AWS region of your Lambda
const region = regions.getRegion();
// To configure a context with the current AWS region
const context = {};
const failFast = true;
regions.configureRegion(context, failFast);
assert(context.region && typeof context.region === 'string');
// To configure stage-handling, which determines the behaviour of the functions numbered 1 to 6 below
const stages = require('aws-core-utils/stages');
const settings = undefined; // ... or your own custom settings
const options = require('aws-core-utils/stages-options.json'); // ... or your own custom options
// ... EITHER using the default stage handling configuration and default logging configuration
stages.configureDefaultStageHandling(context);
// ... OR using the default stage handling configuration and default logging configuration partially customised via stageHandlingOptions, otherSettings & otherOptions
const stageHandlingOptions = require('aws-core-utils/stages-options.json').stageHandlingOptions; // example ONLY - use your own custom stage handling options if needed
const otherSettings = undefined; // or to configure your own underlying logger use: const otherSettings = {loggingSettings: {underlyingLogger: myCustomLogger}};
const otherOptions = require('aws-core-utils/test/sample-standard-options.json'); // example ONLY - use your own custom standard options file
const forceConfiguration = false;
stages.configureDefaultStageHandling(context, stageHandlingOptions, otherSettings, otherOptions, forceConfiguration);
// ... OR using your own custom stage-handling configuration
const stageHandlingSettings = stages.getDefaultStageHandlingSettings(options.stageHandlingOptions);
// Optionally override the default stage handling functions with your own custom functions
// stageHandlingSettings.customToStage = undefined;
// stageHandlingSettings.convertAliasToStage = stages.DEFAULTS.convertAliasToStage;
// stageHandlingSettings.injectStageIntoStreamName = stages.DEFAULTS.toStageSuffixedStreamName;
// stageHandlingSettings.extractStageFromStreamName = stages.DEFAULTS.extractStageFromSuffixedStreamName;
// stageHandlingSettings.injectStageIntoResourceName = stages.DEFAULTS.toStageSuffixedResourceName;
// stageHandlingSettings.extractStageFromResourceName = stages.DEFAULTS.extractStageFromSuffixedResourceName;
stages.configureStageHandling(context, stageHandlingSettings, stageHandlingOptions, otherSettings, otherOptions, forceConfiguration);
// ... OR using completely customised stage handling settings
const stageHandlingSettings2 = {
envStageName: myEnvStageName,
customToStage: myCustomToStageFunction, // or undefined if not needed
convertAliasToStage: myConvertAliasToStageFunction, // or undefined to knockout using AWS aliases as stages
injectStageIntoStreamName: myInjectStageIntoStreamNameFunction,
extractStageFromStreamName: myExtractStageFromStreamNameFunction,
streamNameStageSeparator: myStreamNameStageSeparator,
injectStageIntoResourceName: myInjectStageIntoResourceNameFunction,
extractStageFromResourceName: myExtractStageFromResourceNameFunction,
resourceNameStageSeparator: myResourceNameStageSeparator,
injectInCase: myInjectInCase,
extractInCase: myExtractInCase,
defaultStage: myDefaultStage, // or undefined
};
stages.configureStageHandling(context, stageHandlingSettings2, undefined, otherSettings, otherOptions, forceConfiguration);
// ... OR using custom stage handling settings and/or options and configuring dependencies at the same time
stages.configureStageHandling(context, stageHandlingSettings, stageHandlingOptions, otherSettings, otherOptions, forceConfiguration);
// To check if stage handling is configured
const configured = stages.isStageHandlingConfigured(context);
// To look up stage handling settings and functions
const settingName = 'injectInCase'; // example stage handling setting name
const setting = stages.getStageHandlingSetting(context, settingName);
const functionSettingName = 'convertAliasToStage'; // example stage handling function name
const fn = stages.getStageHandlingFunction(context, functionSettingName);
// 1. To resolve / derive a stage from an AWS event
const context = {};
const stage = stages.resolveStage(awsEvent, awsContext, context);
// 2. To configure a context with a resolved stage (uses resolveStage)
const failFast = true;
stages.configureStage(context, awsEvent, awsContext, failFast);
assert(context.stage && typeof context.stage === 'string');
// 3. To extract a stage from a qualified stream name
const qualifiedStreamName = 'TestStream_PROD';
const stage2 = stages.extractStageFromQualifiedStreamName(qualifiedStreamName, context);
assert(stage2 === 'prod'); // assuming default stage handling configuration
// 4. To qualify an unqualified stream name with a stage
const unqualifiedStreamName = 'TestStream';
const stageQualifiedStreamName = stages.toStageQualifiedStreamName(unqualifiedStreamName, stage2, context);
assert(stageQualifiedStreamName === 'TestStream_PROD'); // assuming default stage handling configuration
// 5. To extract a stage from a qualified resource name
const qualifiedTableName = 'TestTable_QA';
const stage3 = stages.extractStageFromQualifiedResourceName(qualifiedTableName, context);
assert(stage3 === 'qa'); // assuming default stage handling configuration
// 6. To qualify an unqualified resource name with a stage
const unqualifiedTableName = 'TestTable';
const stageQualifiedResourceName = stages.toStageQualifiedResourceName(unqualifiedTableName, stage3, context);
assert(stageQualifiedResourceName === 'TestTable_QA'); // assuming default stage handling configuration
const streamEvents = require('aws-core-utils/stream-events');
// To extract event soure ARNs from AWS events
const eventSourceARNs = streamEvents.getEventSourceARNs(event);
// To extract Kinesis stream names from AWS events and event records
const eventSourceStreamNames = streamEvents.getKinesisEventSourceStreamNames(event);
const eventSourceStreamName = streamEvents.getKinesisEventSourceStreamName(record);
// To extract DynamoDB table names from DynamoDB stream event records
const dynamoDBEventSourceTableName = streamEvents.getDynamoDBEventSourceTableName(record);
// To extract DynamoDB table names and stream timestamps/suffixes from DynamoDB stream event records
const tableNameAndStreamTimestamp = streamEvents.getDynamoDBEventSourceTableNameAndStreamTimestamp(record);
const dynamoDBEventSourceTableName1 = tableNameAndStreamTimestamp[0];
const dynamoDBEventSourceStreamTimestamp = tableNameAndStreamTimestamp[1];
// Simple checks to validate existance of some of the properties of Kinesis & DynamoDB stream event records
try {
streamEvents.validateStreamEventRecord(record);
streamEvents.validateKinesisStreamEventRecord(record);
streamEvents.validateDynamoDBStreamEventRecord(record);
} catch (err) {
// ...
}
This module's unit tests were developed with and must be run with tape. The unit tests have been tested on Node.js v6.10.3.
Install tape globally if you want to run multiple tests at once:
$ npm install tape -g
Run all unit tests with:
$ npm test
or with tape:
$ tape test/*.js
See the package source for more details.
See CHANGES.md
8.1.0
api-lambdas
module:
postConfigure
function, which can be configured onto context.handler
, that can be
used by an AWS Lambda handler
to add any additional configuration needed to the context AFTER the primary
configuration of the context has completed and BEFORE the main function is executedcontext
failure casesstringify
function to better survive responses with circular dependenciesother-lambdas
module:
postConfigure
function, which can be configured onto context.handler
, that can be
used by an AWS Lambda handler
to add any additional configuration needed to the context AFTER the primary
configuration of the context has completed and BEFORE the main function is executedcontext
failure casesstringify
function to better survive responses with circular dependenciesstages
module:
logging
property or the legacy loggingOptions
propertylogging
property or the legacy loggingSettings
propertycontexts
module:
staging
property or the legacy stagingOptions
propertystaging
property or the legacy stagingSettings
propertycustom
property or the legacy customOptions
propertycustom
property or the legacy customSettings
propertyFAQs
Core utilities for working with Amazon Web Services (AWS), including ARNs, regions, stages, Lambdas, AWS errors, stream events, Kinesis, DynamoDB.DocumentClients, etc.
The npm package aws-core-utils receives a total of 24 weekly downloads. As such, aws-core-utils popularity was classified as not popular.
We found that aws-core-utils demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer 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.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.