From 4f1ce4316d84503cf92feeca20e7b8d1f9930fc6 Mon Sep 17 00:00:00 2001 From: Felix Kaiser Date: Sun, 7 Apr 2024 22:46:40 -0700 Subject: [PATCH] Add more input validation --- link2aws.js | 29 +++++++++++++++++++++++++++++ test/test.js | 9 ++++++++- testcases/aws-negative.json | 18 +++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/link2aws.js b/link2aws.js index 5e0b774..6b1e672 100644 --- a/link2aws.js +++ b/link2aws.js @@ -2,8 +2,30 @@ // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/resource-ids.html class ARN { constructor(text) { + if (typeof(text) != 'string') { + throw Error("ARN must be a string"); + } + text = text.trim(); + // length limit + // There is no documented limit for ARNs in general. + // For IAM User, the documented limit is 2048. + // Please file an issue if you can find a resource type + // with a higher documented limit. + if (text.length > 2048) { + throw Error("ARN too long"); + } + + // Check for invalid characters. + // This is meant to catch malicious inputs. This will not + // catch all invalid ARNs, as some resource types have + // stricter rules. Please file an issue if you are aware + // of a valid ARN that is rejected by this check. + if (!/^[a-zA-Z0-9:/+=,.@_*#\-]*$/.test(text)) { + throw Error("ARN contains invalid characters"); + } + // split into tokens; leaving resource-id with colons together var firstTokens = text.split(':'); var tokens = firstTokens.splice(0, 6); @@ -54,6 +76,13 @@ class ARN { throw Error("Bad number of tokens"); } + // region must have valid format. + // This is security relevant as it is used as a subdomain + // before the console domain. + if (this.region != '' && !/^[a-z0-9-]*$/.test(this.region)) { + throw Error(`Bad region: "${this.region}"`); + } + this._linkTemplates = this._getLinkTemplates(); } diff --git a/test/test.js b/test/test.js index aba2db9..bdb62ce 100644 --- a/test/test.js +++ b/test/test.js @@ -8,7 +8,14 @@ var stringTests = require('../testcases/string.json'); describe('main.ARN', function () { describe('#constructor(text)', function () { it('should reject invalid inputs by throwing', function () { - assert.throws(() => { main.ARN('foo') }, Error); + assert.throws(() => { new main.ARN('foo') }, Error); + }); + + it('should reject arguments that aren\'t strings', function () { + assert.throws(() => { new main.ARN(null) }, Error); + assert.throws(() => { new main.ARN(123) }, Error); + assert.throws(() => { new main.ARN([]) }, Error); + assert.throws(() => { new main.ARN({}) }, Error); }); it('should tokenize ARNs without resource-type', function () { diff --git a/testcases/aws-negative.json b/testcases/aws-negative.json index a2e4f93..21c1b3b 100644 --- a/testcases/aws-negative.json +++ b/testcases/aws-negative.json @@ -22,5 +22,21 @@ "arn:aws:acm:us-east-1:123456789012:UNSUPPORTED/1f6ee793-4064-4a10-9567-f03875640b35", - "arn:aws:ec2:us-east-1:123456789012:UNSUPPORTED/1234" + "arn:aws:ec2:us-east-1:123456789012:UNSUPPORTED/1234", + + "arn:aws-us-gov:iam::123456789012:user''''''test", + + "arn:aws:ec2:us-east-1:123456789012:instance/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy", + + "arn:aws:ec2:us-ea:st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea/st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea+st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea=st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea,st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea.st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea@st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea*st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea#st-1:123456789012:instance/asdf", + "arn:aws:ec2:us-ea\\st-1:123456789012:instance/asdf" ] \ No newline at end of file