Skip to content

Commit b4d9155

Browse files
authored
fix: correctly emit quoted YAML for account numbers (#1105)
Switch back to the newly-fixed 'yaml' package so that we can get both correct quoting of strings with leading '0' characters and correct quoting of strings with colon ':' characters. Fixes #1100, fixes #1098.
1 parent cafcc11 commit b4d9155

File tree

8 files changed

+73
-42
lines changed

8 files changed

+73
-42
lines changed

packages/@aws-cdk/applet-js/bin/cdk-applet-js.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import 'source-map-support/register';
44
import cdk = require('@aws-cdk/cdk');
55
import child_process = require('child_process');
66
import fs = require('fs-extra');
7-
import YAML = require('js-yaml');
87
import os = require('os');
98
import path = require('path');
9+
import YAML = require('yaml');
1010

1111
import { isStackConstructor, parseApplet } from '../lib/applet-helpers';
1212

@@ -25,7 +25,7 @@ async function main() {
2525
}
2626

2727
// read applet(s) properties from the provided file
28-
const fileContents = YAML.safeLoad(await fs.readFile(appletFile, { encoding: 'utf-8' }));
28+
const fileContents = YAML.parse(await fs.readFile(appletFile, { encoding: 'utf-8' }));
2929
if (typeof fileContents !== 'object') {
3030
throw new Error(`${appletFile}: should contain a YAML object`);
3131
}

packages/@aws-cdk/applet-js/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@
2323
"license": "Apache-2.0",
2424
"devDependencies": {
2525
"@types/fs-extra": "^5.0.4",
26-
"@types/js-yaml": "^3.11.2",
26+
"@types/yaml": "^1.0.0",
2727
"cdk-build-tools": "^0.15.1",
2828
"pkglint": "^0.15.1"
2929
},
3030
"dependencies": {
3131
"@aws-cdk/cdk": "^0.15.1",
3232
"fs-extra": "^7.0.0",
3333
"source-map-support": "^0.5.6",
34-
"js-yaml": "^3.12.0"
34+
"yaml": "^1.0.1"
3535
},
3636
"repository": {
3737
"url": "https://github.com/awslabs/aws-cdk.git",

packages/aws-cdk/bin/cdk.ts

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import 'source-map-support/register';
44
import cxapi = require('@aws-cdk/cx-api');
55
import colors = require('colors/safe');
66
import fs = require('fs-extra');
7-
import YAML = require('js-yaml');
87
import minimatch = require('minimatch');
98
import util = require('util');
109
import yargs = require('yargs');
@@ -19,6 +18,7 @@ import { interactive } from '../lib/interactive';
1918
import { data, debug, error, highlight, print, setVerbose, success, warning } from '../lib/logging';
2019
import { PluginHost } from '../lib/plugin';
2120
import { parseRenames } from '../lib/renames';
21+
import { deserializeStructure, serializeStructure } from '../lib/serialize';
2222
import { DEFAULTS, PER_USER_DEFAULTS, Settings } from '../lib/settings';
2323
import { VERSION } from '../lib/version';
2424

@@ -609,11 +609,7 @@ async function initCommandLine() {
609609
610610
/* Attempt to parse YAML, fall back to JSON. */
611611
function parseTemplate(text: string): any {
612-
try {
613-
return YAML.safeLoad(text);
614-
} catch (e) {
615-
return JSON.parse(text);
616-
}
612+
return deserializeStructure(text);
617613
}
618614
}
619615
@@ -679,13 +675,7 @@ async function initCommandLine() {
679675
}
680676

681677
function toJsonOrYaml(object: any): string {
682-
if (argv.json) {
683-
const noFiltering = undefined;
684-
const indentWidth = 2;
685-
return JSON.stringify(object, noFiltering, indentWidth);
686-
} else {
687-
return YAML.safeDump(object);
688-
}
678+
return serializeStructure(object, argv.json);
689679
}
690680
}
691681

packages/aws-cdk/integ-tests/test-cdk-synth.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ setup
88

99
assert "cdk synth cdk-toolkit-integration-test-1" <<HERE
1010
Resources:
11-
topic69831491:
12-
Type: 'AWS::SNS::Topic'
11+
topic69831491:
12+
Type: AWS::SNS::Topic
1313
1414
HERE
1515

1616
assert "cdk synth cdk-toolkit-integration-test-2" <<HERE
1717
Resources:
18-
topic152D84A37:
19-
Type: 'AWS::SNS::Topic'
20-
topic2A4FB547F:
21-
Type: 'AWS::SNS::Topic'
18+
topic152D84A37:
19+
Type: AWS::SNS::Topic
20+
topic2A4FB547F:
21+
Type: AWS::SNS::Topic
2222
2323
HERE
2424

packages/aws-cdk/lib/api/deploy-stack.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import cxapi = require('@aws-cdk/cx-api');
22
import aws = require('aws-sdk');
33
import colors = require('colors/safe');
4-
import YAML = require('js-yaml');
54
import uuid = require('uuid');
65
import { prepareAssets } from '../assets';
76
import { debug, error, print } from '../logging';
7+
import { toYAML } from '../serialize';
88
import { Mode } from './aws-auth/credentials';
99
import { ToolkitInfo } from './toolkit-info';
1010
import { describeStack, stackExists, stackFailedCreating, waitForChangeSet, waitForStack } from './util/cloudformation';
@@ -113,7 +113,7 @@ async function getStackOutputs(cfn: aws.CloudFormation, stackName: string): Prom
113113
* @param toolkitInfo information about the toolkit stack
114114
*/
115115
async function makeBodyParameter(stack: cxapi.SynthesizedStack, toolkitInfo?: ToolkitInfo): Promise<TemplateBodyParameter> {
116-
const templateJson = YAML.safeDump(stack.template, { indent: 4, flowLevel: 16 });
116+
const templateJson = toYAML(stack.template);
117117
if (toolkitInfo) {
118118
const s3KeyPrefix = `cdk/${stack.name}/`;
119119
const s3KeySuffix = '.yml';

packages/aws-cdk/lib/serialize.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import YAML = require('yaml');
2+
3+
/**
4+
* Stringify to YAML
5+
*/
6+
export function toYAML(obj: any): string {
7+
return YAML.stringify(obj, { schema: 'yaml-1.1' });
8+
}
9+
10+
/**
11+
* Parse YAML
12+
*/
13+
export function fromYAML(str: string): any {
14+
return YAML.parse(str, { schema: 'yaml-1.1' });
15+
}
16+
17+
/**
18+
* Parse either YAML or JSON
19+
*/
20+
export function deserializeStructure(str: string) {
21+
try {
22+
return fromYAML(str);
23+
} catch (e) {
24+
// This shouldn't really ever happen I think, but it's the code we had so I'm leaving it.
25+
return JSON.parse(str);
26+
}
27+
}
28+
29+
/**
30+
* Serialize to either YAML or JSON
31+
*/
32+
export function serializeStructure(object: any, json: boolean) {
33+
if (json) {
34+
return JSON.stringify(object, undefined, 2);
35+
} else {
36+
return toYAML(object);
37+
}
38+
}

packages/aws-cdk/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"@types/semver": "^5.5.0",
4040
"@types/uuid": "^3.4.3",
4141
"@types/yargs": "^8.0.3",
42-
"@types/js-yaml": "^3.11.2",
42+
"@types/yaml": "^1.0.0",
4343
"cdk-build-tools": "^0.15.1",
4444
"mockery": "^2.1.0",
4545
"pkglint": "^0.15.1"
@@ -54,7 +54,7 @@
5454
"colors": "^1.2.1",
5555
"decamelize": "^2.0.0",
5656
"fs-extra": "^4.0.2",
57-
"js-yaml": "^3.12.0",
57+
"yaml": "^1.0.1",
5858
"json-diff": "^0.3.1",
5959
"minimatch": ">=3.0",
6060
"promptly": "^0.2.0",

packages/aws-cdk/test/test.yaml.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,47 @@
11
import { Test } from 'nodeunit';
2+
import { toYAML } from '../lib/serialize';
23

3-
import YAML = require('js-yaml');
4-
5-
function yamlStringify(obj: any) {
6-
return YAML.dump(obj);
7-
}
4+
// Preferred quote of the YAML library
5+
const q = '"';
86

97
export = {
108
'quote the word "ON"'(test: Test) {
119
// NON NEGOTIABLE! If not quoted, will be interpreted as the boolean TRUE
1210

1311
// tslint:disable-next-line:no-console
14-
const output = yamlStringify({
12+
const output = toYAML({
1513
notABoolean: "ON"
1614
});
1715

18-
test.equals(output.trim(), `notABoolean: 'ON'`);
16+
test.equals(output.trim(), `notABoolean: ${q}ON${q}`);
1917

2018
test.done();
2119
},
2220

2321
'quote number-like strings with a leading 0'(test: Test) {
24-
const output = yamlStringify({
22+
const output = toYAML({
2523
leadingZero: "012345"
2624
});
2725

28-
test.equals(output.trim(), `leadingZero: '012345'`);
26+
test.equals(output.trim(), `leadingZero: ${q}012345${q}`);
2927

3028
test.done();
3129
},
3230

3331
'do not quote octal numbers that arent really octal'(test: Test) {
34-
// Under contention: this seems to be okay, pyyaml parses it
35-
// correctly. Unsure of what CloudFormation does about it.
32+
// This is a contentious one, and something that might have changed in YAML1.2 vs YAML1.1
33+
//
34+
// One could make the argument that a sequence of characters that couldn't ever
35+
// be an octal value doesn't need to be quoted, and pyyaml parses it correctly.
36+
//
37+
// However, CloudFormation's parser interprets it as a decimal number (eating the
38+
// leading 0) if it's unquoted, so that's the behavior we're testing for.
3639

37-
const output = yamlStringify({
40+
const output = toYAML({
3841
leadingZero: "0123456789"
3942
});
4043

41-
test.equals(output.trim(), `leadingZero: 0123456789`);
44+
test.equals(output.trim(), `leadingZero: ${q}0123456789${q}`);
4245

4346
test.done();
4447
},
@@ -48,14 +51,14 @@ export = {
4851
//
4952
// 'yaml' fails this.
5053

51-
const output = yamlStringify({
54+
const output = toYAML({
5255
colons: ['arn', ':', 'aws']
5356
});
5457

5558
test.equals(output.trim(), [
5659
'colons:',
5760
' - arn',
58-
` - ':'`,
61+
` - ${q}:${q}`,
5962
' - aws'
6063
].join('\n'));
6164

0 commit comments

Comments
 (0)