Skip to content
This repository has been archived by the owner on Apr 16, 2022. It is now read-only.

Issue #140 - Fixed parsing of parameter values that contain commas that have been properly escaped using a backslash. #141

Merged
merged 6 commits into from
Apr 21, 2018
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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic
Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- Merge PR #141, allowing lists of values to be specified on the command line

## [1.6.0] - 2018-04-05
### Fixed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ Template invalid!

`--verbose`: Provide verbose output and stack traces when template parsing fails.

** Note ** : Parameter values that contain commas must be escaped using a backslash (e.g. `Param1=[1\,2\,3]`). Depending on your command-line interpreter you may have to use double backslashes or enclose the entire argument in quotes, such is the case with *Bash* and other *Bourne*-based shells (e.g. `--parameters 'Param1=[1\,2\,3]'`).

### What can cfn-lint do?
* Read JSON + YAML (Including YAML short form)
* Detect invalid property names
Expand Down
14 changes: 12 additions & 2 deletions data/aws_parameter_types.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"String": "string",
"Number": "number",
"List<Number>": "array",
"CommaDelimitedList": "array",
"AWS::EC2::AvailabilityZone::Name": "string",
"AWS::EC2::Image::Id": "string",
Expand All @@ -11,5 +12,14 @@
"AWS::EC2::Subnet::Id": "string",
"AWS::EC2::Volume::Id": "string",
"AWS::EC2::VPC::Id": "string",
"AWS::Route53::HostedZone::Id": "string"
}
"AWS::Route53::HostedZone::Id": "string",
"List<AWS::EC2::AvailabilityZone::Name>": "array",
"List<AWS::EC2::Image::Id>": "array",
"List<AWS::EC2::Instance::Id>": "array",
"List<AWS::EC2::SecurityGroup::GroupName>": "array",
"List<AWS::EC2::SecurityGroup::Id>": "array",
"List<AWS::EC2::Subnet::Id>": "array",
"List<AWS::EC2::Volume::Id>": "array",
"List<AWS::EC2::VPC::Id>": "array",
"List<AWS::Route53::HostedZone::Id>": "array"
}
16 changes: 15 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,22 @@ let version = require('../package').version;
let firstArg: string | undefined = undefined
let secondArg: string = undefined!;

/**
* Used for parsing comma separated commandline argument values whilst, taking into account backslash escapes.
* Returns an array of strings (e.g. ["Arg1=Val1", "Arg2=Val2"]).
*/
function list(val: string) {
return val.split(',');
// prepare for a negated lookahead
val = val.replace(/\\,/g, ',\\');

// split and remove escapes
return val.split(/,(?!\\)/g)
.map((x) => {
return x.replace(/,\\/g, ',');
})
.filter((x) => {
return !!x;
});
}

program
Expand Down
18 changes: 18 additions & 0 deletions src/test/indexTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,24 @@ describe('index', () => {
done();
});
}).timeout(5000);

it('parameters of the CommaDelimitedList type should accept lists as values', (done) => {
exec('node lib/index.js validate testData/valid/yaml/parameters_type_commadelimitedlist.yaml --parameters SomeParam="test\\,dev\\,prod"', function(error, stdout, stderr) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be mentioned in the docs?

something,something,something is possible

expect(stdout).to.contain('0 warn');
expect(stdout).to.contain('0 crit');
expect(stderr).to.be.empty;
done();
});
}).timeout(5000);

it('parameters of the List<Number> type should accept lists as values', (done) => {
exec('node lib/index.js validate testData/valid/yaml/parameters_type_list_number.yaml --parameters SomeParam="1\\,2\\,3"', function(error, stdout, stderr) {
expect(stdout).to.contain('0 warn');
expect(stdout).to.contain('0 crit');
expect(stderr).to.be.empty;
done();
});
}).timeout(5000);
});

});
81 changes: 81 additions & 0 deletions src/test/validatorTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,87 @@ describe('validator', () => {
expect(result['errors']['crit']).to.have.lengthOf(0);
expect(result['errors']['info']).to.have.lengthOf(0);
})

it('List<AWS::EC2::AvailabilityZone::Name> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_availabilityzone_name.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::Image::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_image_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::Instance::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_instance_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::SecurityGroup::GroupName> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_securitygroup_groupname.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::SecurityGroup::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_securitygroup_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::Subnet::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_subnet_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::Volume::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_volume_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::EC2::VPC::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_ec2_vpc_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});

it('List<AWS::Route53::HostedZone::Id> should return a list', () => {
const input = './testData/valid/yaml/parameters_type_list_aws_route53_hostedzone_id.yaml';
let result = validator.validateFile(input);
expect(result).to.have.deep.property('templateValid', true);
expect(result['errors']['info']).to.have.lengthOf(0);
expect(result['errors']['warn']).to.have.lengthOf(0);
expect(result['errors']['crit']).to.have.lengthOf(0);
});
});

describe('pseudo-parmeters', () => {
Expand Down
11 changes: 11 additions & 0 deletions src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ function assignParametersOutput(guessParameters?: string[]) {
})
}

// The List<type> parameter value is inferred as string with comma delimited values and must be converted to array
let listParameterTypesSpec = Object.keys(parameterTypesSpec).filter((x) => !!x.match(/List<.*>/));
if (!!~listParameterTypesSpec.indexOf(parameter['Type']) && (typeof parameterValue === 'string')) {
parameterValue = parameterValue.split(',').map(x => x.trim());
parameterValue.forEach(val => {
if (val === ""){
addError('crit', `Parameter ${parameterName} contains a List<${parameter['Type']}> where the number of commas appears to be equal or greater than the list of items.`, ['Parameters', parameterName], "Parameters");
}
})
}

// Assign an Attribute Ref regardless of any failures above
workingInput['Parameters'][parameterName]['Attributes'] = {};
workingInput['Parameters'][parameterName]['Attributes']['Ref'] = parameterValue;
Expand Down
11 changes: 11 additions & 0 deletions testData/valid/yaml/parameters_type_commadelimitedlist.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Parameters:
SomeParam:
Type: CommaDelimitedList
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 2
- Ref: SomeParam
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::AvailabilityZone::Name>
Default: us-west-2a, us-west-2b
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
12 changes: 12 additions & 0 deletions testData/valid/yaml/parameters_type_list_aws_ec2_image_id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::Image::Id>
Default: ami-ff527ecf, ami-e7527ed7
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
12 changes: 12 additions & 0 deletions testData/valid/yaml/parameters_type_list_aws_ec2_instance_id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::Instance::Id>
Default: i-1e731a32, i-1e731a34
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::SecurityGroup::GroupName>
Default: my-sg-abc, my-sg-def
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::SecurityGroup::Id>
Default: sg-a123fd85, sg-b456fd85
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
12 changes: 12 additions & 0 deletions testData/valid/yaml/parameters_type_list_aws_ec2_subnet_id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::Subnet::Id>
Default: subnet-123a351e, subnet-456b351e
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
12 changes: 12 additions & 0 deletions testData/valid/yaml/parameters_type_list_aws_ec2_volume_id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::Volume::Id>
Default: vol-3cdd3f56, vol-4cdd3f56
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
12 changes: 12 additions & 0 deletions testData/valid/yaml/parameters_type_list_aws_ec2_vpc_id.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::EC2::VPC::Id>
Default: vpc-a123baa3, vpc-b456baa3
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Parameters:
SomeParam:
Type: List<AWS::Route53::HostedZone::Id>
Default: Z23YXV4OVPL04A, Z23YXV4OVPL04B
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 1
- Ref: SomeParam
11 changes: 11 additions & 0 deletions testData/valid/yaml/parameters_type_list_number.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Parameters:
SomeParam:
Type: List<Number>
Resources:
bucket_name:
Type: AWS::S3::Bucket
Properties:
BucketName:
Fn::Select:
- 2
- Ref: SomeParam