Compare commits
2 Commits
developmen
...
w/8.0/feat
Author | SHA1 | Date |
---|---|---|
bert-e | 8d9507a5c8 | |
Dora Korpar | a7ac5fefe3 |
|
@ -467,6 +467,10 @@
|
|||
"code": 400,
|
||||
"description": "The request was rejected because an invalid or out-of-range value was supplied for an input parameter."
|
||||
},
|
||||
"MalformedPolicy": {
|
||||
"code": 400,
|
||||
"description": "Policies must be valid JSON and the first byte must be '{'"
|
||||
},
|
||||
"_comment": "-------------- Special non-AWS S3 errors --------------",
|
||||
"MPUinProgress": {
|
||||
"code": 409,
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
const Ajv = require('ajv');
|
||||
const userPolicySchema = require('./userPolicySchema');
|
||||
const resourcePolicySchema = require('./resourcePolicySchema');
|
||||
const errors = require('../errors');
|
||||
|
||||
const ajValidate = new Ajv({ allErrors: true });
|
||||
// compiles schema to functions and caches them for all cases
|
||||
const userPolicyValidate = ajValidate.compile(userPolicySchema);
|
||||
const resourcePolicyValidate = ajValidate.compile(resourcePolicySchema);
|
||||
|
||||
const errDict = {
|
||||
required: {
|
||||
|
@ -24,33 +26,39 @@ const errDict = {
|
|||
};
|
||||
|
||||
// parse ajv errors and return early with the first relevant error
|
||||
function _parseErrors(ajvErrors) {
|
||||
function _parseErrors(ajvErrors, policyType) {
|
||||
let parsedErr;
|
||||
if (policyType === 'user') {
|
||||
// deep copy is needed as we have to assign custom error description
|
||||
const parsedErr = Object.assign({}, errors.MalformedPolicyDocument);
|
||||
parsedErr = Object.assign({}, errors.MalformedPolicyDocument);
|
||||
parsedErr.description = 'Syntax errors in policy.';
|
||||
}
|
||||
if (policyType === 'resource') {
|
||||
parsedErr = Object.assign({}, errors.MalformedPolicy);
|
||||
}
|
||||
ajvErrors.some(err => {
|
||||
const resource = err.dataPath;
|
||||
const field = err.params ? err.params.missingProperty : undefined;
|
||||
const errType = err.keyword;
|
||||
if (errType === 'type' && (resource === '.Statement' ||
|
||||
resource === '.Statement.Resource' ||
|
||||
resource === '.Statement.NotResource')) {
|
||||
resource.includes('.Resource') ||
|
||||
resource.includes('.NotResource'))) {
|
||||
// skip this as this doesn't have enough error context
|
||||
return false;
|
||||
}
|
||||
if (err.keyword === 'required' && field && errDict.required[field]) {
|
||||
parsedErr.description = errDict.required[field];
|
||||
} else if (err.keyword === 'pattern' &&
|
||||
(resource === '.Statement.Action' ||
|
||||
resource === '.Statement.NotAction')) {
|
||||
(resource.includes('.Action') ||
|
||||
resource.includes('.NotAction'))) {
|
||||
parsedErr.description = errDict.pattern.Action;
|
||||
} else if (err.keyword === 'pattern' &&
|
||||
(resource === '.Statement.Resource' ||
|
||||
resource === '.Statement.NotResource')) {
|
||||
(resource.includes('.Resource') ||
|
||||
resource.includes('.NotResource'))) {
|
||||
parsedErr.description = errDict.pattern.Resource;
|
||||
} else if (err.keyword === 'minItems' &&
|
||||
(resource === '.Statement.Resource' ||
|
||||
resource === '.Statement.NotResource')) {
|
||||
(resource.includes('.Resource') ||
|
||||
resource.includes('.NotResource'))) {
|
||||
parsedErr.description = errDict.minItems.Resource;
|
||||
}
|
||||
return true;
|
||||
|
@ -77,12 +85,24 @@ function _validatePolicy(type, policy) {
|
|||
}
|
||||
userPolicyValidate(parseRes);
|
||||
if (userPolicyValidate.errors) {
|
||||
return { error: _parseErrors(userPolicyValidate.errors),
|
||||
return { error: _parseErrors(userPolicyValidate.errors, 'user'),
|
||||
valid: false };
|
||||
}
|
||||
return { error: null, valid: true };
|
||||
}
|
||||
// TODO: add support for resource policies
|
||||
if (type === 'resource') {
|
||||
const parseRes = _safeJSONParse(policy);
|
||||
if (parseRes instanceof Error) {
|
||||
return { error: Object.assign({}, errors.MalformedPolicy),
|
||||
valid: false };
|
||||
}
|
||||
resourcePolicyValidate(parseRes);
|
||||
if (resourcePolicyValidate.errors) {
|
||||
return { error: _parseErrors(resourcePolicyValidate.errors,
|
||||
'resource'), valid: false };
|
||||
}
|
||||
return { error: null, valid: true };
|
||||
}
|
||||
return { error: errors.NotImplemented, valid: false };
|
||||
}
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"title": "AWS Bucket Policy schema.",
|
||||
"description": "This schema describes a bucket policy per AWS policy grammar rules",
|
||||
"definitions": {
|
||||
"principalService": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Service": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"backbeat"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"principalCanonicalUser": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"CanonicalUser": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-z]{64}$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"principalAnonymous": {
|
||||
"type": "string",
|
||||
"pattern": "^\\*$"
|
||||
},
|
||||
"principalAWSAccountID": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{12}$"
|
||||
},
|
||||
"principalAWSAccountArn": {
|
||||
"type": "string",
|
||||
"pattern": "^arn:aws:iam::[0-9]{12}:root$"
|
||||
},
|
||||
"principalAWSUserArn": {
|
||||
"type": "string",
|
||||
"pattern": "^arn:aws:iam::[0-9]{12}:user/[\\w+=,.@ -]{1,64}$"
|
||||
},
|
||||
"principalAWSRoleArn": {
|
||||
"type": "string",
|
||||
"pattern": "^arn:aws:iam::[0-9]{12}:role/[\\w+=,.@ -]{1,64}$"
|
||||
},
|
||||
"principalAWSItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"AWS": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/principalAWSAccountID" },
|
||||
{ "$ref": "#/definitions/principalAnonymous" },
|
||||
{ "$ref": "#/definitions/principalAWSAccountArn" },
|
||||
{ "$ref": "#/definitions/principalAWSUserArn" },
|
||||
{ "$ref": "#/definitions/principalAWSRoleArn" },
|
||||
{
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/definitions/principalAWSAccountID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/definitions/principalAWSAccountArn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/definitions/principalAWSRoleArn"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"$ref": "#/definitions/principalAWSUserArn"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"principalItem": {
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/principalAWSItem" },
|
||||
{ "$ref": "#/definitions/principalAnonymous" },
|
||||
{ "$ref": "#/definitions/principalService" },
|
||||
{ "$ref": "#/definitions/principalCanonicalUser" }
|
||||
]
|
||||
},
|
||||
"actionItem": {
|
||||
"type": "string",
|
||||
"pattern": "^[^*:]+:([^:])+|^\\*$"
|
||||
},
|
||||
"resourceItem": {
|
||||
"type": "string",
|
||||
"pattern": "^\\*|arn:(aws|scality)(:(\\*{1}|[a-z0-9\\*\\-]{2,})*?){3}:((?!\\$\\{\\}).)*?$"
|
||||
},
|
||||
"conditionKeys" : {
|
||||
"properties": {
|
||||
"aws:CurrentTime": {},
|
||||
"aws:EpochTime": {},
|
||||
"aws:MultiFactorAuthAge": {},
|
||||
"aws:MultiFactorAuthPresent": {},
|
||||
"aws:PrincipalArn": {},
|
||||
"aws:PrincipalOrgId": {},
|
||||
"aws:PrincipalTag/${TagKey}": {},
|
||||
"aws:PrincipalType": {},
|
||||
"aws:Referer": {},
|
||||
"aws:RequestTag/${TagKey}": {},
|
||||
"aws:RequestedRegion": {},
|
||||
"aws:SecureTransport": {},
|
||||
"aws:SourceAccount": {},
|
||||
"aws:SourceArn": {},
|
||||
"aws:SourceIp": {},
|
||||
"aws:SourceVpc": {},
|
||||
"aws:SourceVpce": {},
|
||||
"aws:TagKeys": {},
|
||||
"aws:TokenIssueTime": {},
|
||||
"aws:UserAgent": {},
|
||||
"aws:userid": {},
|
||||
"aws:username": {},
|
||||
"s3:ExistingJobOperation": {},
|
||||
"s3:ExistingJobPriority": {},
|
||||
"s3:ExistingObjectTag/<key>": {},
|
||||
"s3:JobSuspendedCause": {},
|
||||
"s3:LocationConstraint": {},
|
||||
"s3:RequestJobOperation": {},
|
||||
"s3:RequestJobPriority": {},
|
||||
"s3:RequestObjectTag/<key>": {},
|
||||
"s3:RequestObjectTagKeys": {},
|
||||
"s3:VersionId": {},
|
||||
"s3:authtype": {},
|
||||
"s3:delimiter": {},
|
||||
"s3:locationconstraint": {},
|
||||
"s3:max-keys": {},
|
||||
"s3:object-lock-legal-hold": {},
|
||||
"s3:object-lock-mode": {},
|
||||
"s3:object-lock-remaining-retention-days": {},
|
||||
"s3:object-lock-retain-until-date": {},
|
||||
"s3:prefix": {},
|
||||
"s3:signatureage": {},
|
||||
"s3:signatureversion": {},
|
||||
"s3:versionid": {},
|
||||
"s3:x-amz-acl": {},
|
||||
"s3:x-amz-content-sha256": {},
|
||||
"s3:x-amz-copy-source": {},
|
||||
"s3:x-amz-grant-full-control": {},
|
||||
"s3:x-amz-grant-read": {},
|
||||
"s3:x-amz-grant-read-acp": {},
|
||||
"s3:x-amz-grant-write": {},
|
||||
"s3:x-amz-grant-write-acp": {},
|
||||
"s3:x-amz-metadata-directive": {},
|
||||
"s3:x-amz-server-side-encryption": {},
|
||||
"s3:x-amz-server-side-encryption-aws-kms-key-id": {},
|
||||
"s3:x-amz-storage-class": {},
|
||||
"s3:x-amz-website-redirect-location": {}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"conditions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ArnEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnLike": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnLikeIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnNotEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnNotEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnNotLike": {
|
||||
"type": "object"
|
||||
},
|
||||
"ArnNotLikeIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"BinaryEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"BinaryEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"BinaryNotEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"BinaryNotEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"Bool": {
|
||||
"type": "object"
|
||||
},
|
||||
"BoolIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateGreaterThan": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateGreaterThanEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateGreaterThanEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateGreaterThanIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateLessThan": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateLessThanEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateLessThanEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateLessThanIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateNotEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"DateNotEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"IpAddress": {
|
||||
"type": "object"
|
||||
},
|
||||
"IpAddressIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"NotIpAddress": {
|
||||
"type": "object"
|
||||
},
|
||||
"NotIpAddressIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"Null": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericGreaterThan": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericGreaterThanEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericGreaterThanEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericGreaterThanIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericLessThan": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericLessThanEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericLessThanEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericLessThanIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericNotEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"NumericNotEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringEqualsIgnoreCase": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringEqualsIgnoreCaseIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringLike": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringLikeIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringNotEquals": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringNotEqualsIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringNotEqualsIgnoreCase": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringNotEqualsIgnoreCaseIfExists": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringNotLike": {
|
||||
"type": "object"
|
||||
},
|
||||
"StringNotLikeIfExists": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"Version": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"2012-10-17"
|
||||
]
|
||||
},
|
||||
"Statement": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": [
|
||||
"array"
|
||||
],
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Sid": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9]+$"
|
||||
},
|
||||
"Action": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/actionItem"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/actionItem"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Effect": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Allow",
|
||||
"Deny"
|
||||
]
|
||||
},
|
||||
"Principal": {
|
||||
"$ref": "#/definitions/principalItem"
|
||||
},
|
||||
"Resource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resourceItem"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/resourceItem"
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$ref": "#/definitions/conditions"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Action",
|
||||
"Effect",
|
||||
"Principal",
|
||||
"Resource"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": [
|
||||
"object"
|
||||
],
|
||||
"properties": {
|
||||
"Sid": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9]+$"
|
||||
},
|
||||
"Action": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/actionItem"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/actionItem"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Effect": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Allow",
|
||||
"Deny"
|
||||
]
|
||||
},
|
||||
"Principal": {
|
||||
"$ref": "#/definitions/principalItem"
|
||||
},
|
||||
"Resource": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/resourceItem"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/resourceItem"
|
||||
},
|
||||
"minItems": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$ref": "#/definitions/conditions"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Action",
|
||||
"Effect",
|
||||
"Resource",
|
||||
"Principal"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Version",
|
||||
"Statement"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"type": "object",
|
||||
"title": "AWS Policy schema.",
|
||||
"title": "AWS IAM Policy schema.",
|
||||
"description": "This schema describes a user policy per AWS policy grammar rules",
|
||||
"definitions": {
|
||||
"principalService": {
|
||||
|
|
|
@ -32,6 +32,9 @@ const _actionMap = {
|
|||
bucketPutLifecycle: 's3:PutLifecycleConfiguration',
|
||||
bucketGetLifecycle: 's3:GetLifecycleConfiguration',
|
||||
bucketDeleteLifecycle: 's3:DeleteLifecycleConfiguration',
|
||||
bucketPutPolicy: 's3:PutBucketPolicy',
|
||||
bucketGetPolicy: 's3:GetBucketPolicy',
|
||||
bucketDeletePolicy: 's3:DeleteBucketPolicy',
|
||||
completeMultipartUpload: 's3:PutObject',
|
||||
initiateMultipartUpload: 's3:PutObject',
|
||||
listMultipartUploads: 's3:ListBucketMultipartUploads',
|
||||
|
|
|
@ -4,8 +4,9 @@ const assert = require('assert');
|
|||
const policyValidator = require('../../../lib/policy/policyValidator');
|
||||
const errors = require('../../../lib/errors');
|
||||
const validateUserPolicy = policyValidator.validateUserPolicy;
|
||||
const validateResourcePolicy = policyValidator.validateResourcePolicy;
|
||||
const successRes = { error: null, valid: true };
|
||||
const samplePolicy = {
|
||||
const sampleUserPolicy = {
|
||||
Version: '2012-10-17',
|
||||
Statement: {
|
||||
Sid: 'FooBar1234',
|
||||
|
@ -15,6 +16,19 @@ const samplePolicy = {
|
|||
Condition: { NumericLessThanEquals: { 's3:max-keys': '10' } },
|
||||
},
|
||||
};
|
||||
const sampleResourcePolicy = {
|
||||
Version: '2012-10-17',
|
||||
Statement: [
|
||||
{
|
||||
Sid: 'ResourcePolicy1',
|
||||
Effect: 'Allow',
|
||||
Action: 's3:ListBucket',
|
||||
Resource: 'arn:aws:s3:::example-bucket',
|
||||
Condition: { StringLike: { 's3:prefix': 'foo' } },
|
||||
Principal: '*',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const errDict = {
|
||||
required: {
|
||||
|
@ -30,45 +44,84 @@ const errDict = {
|
|||
Resource: 'Policy statement must contain resources.',
|
||||
},
|
||||
};
|
||||
let policy;
|
||||
|
||||
function failRes(errDescription) {
|
||||
const error = Object.assign({}, errors.MalformedPolicyDocument);
|
||||
function failRes(policyType, errDescription) {
|
||||
let error;
|
||||
if (policyType === 'user') {
|
||||
error = Object.assign({}, errors.MalformedPolicyDocument);
|
||||
}
|
||||
if (policyType === 'resource') {
|
||||
error = Object.assign({}, errors.MalformedPolicy);
|
||||
}
|
||||
error.description = errDescription || error.description;
|
||||
return { error, valid: false };
|
||||
}
|
||||
|
||||
function check(input, expected) {
|
||||
const result = validateUserPolicy(JSON.stringify(input));
|
||||
function check(input, expected, policyType) {
|
||||
let result;
|
||||
if (policyType === 'user') {
|
||||
result = validateUserPolicy(JSON.stringify(input));
|
||||
}
|
||||
if (policyType === 'resource') {
|
||||
result = validateResourcePolicy(JSON.stringify(input));
|
||||
}
|
||||
assert.deepStrictEqual(result, expected);
|
||||
}
|
||||
|
||||
let userPolicy;
|
||||
let resourcePolicy;
|
||||
const user = 'user';
|
||||
const resource = 'resource';
|
||||
|
||||
beforeEach(() => {
|
||||
policy = JSON.parse(JSON.stringify(samplePolicy));
|
||||
userPolicy = JSON.parse(JSON.stringify(sampleUserPolicy));
|
||||
resourcePolicy = JSON.parse(JSON.stringify(sampleResourcePolicy));
|
||||
});
|
||||
|
||||
describe('Policies validation - Invalid JSON', () => {
|
||||
it('should return error for invalid JSON', () => {
|
||||
it('should return error for invalid user policy JSON', () => {
|
||||
const result = validateUserPolicy('{"Version":"2012-10-17",' +
|
||||
'"Statement":{"Effect":"Allow""Action":"s3:PutObject",' +
|
||||
'"Resource":"arn:aws:s3*"}}');
|
||||
assert.deepStrictEqual(result, failRes());
|
||||
assert.deepStrictEqual(result, failRes(user));
|
||||
});
|
||||
it('should return error for invaild resource policy JSON', () => {
|
||||
const result = validateResourcePolicy('{"Version":"2012-10-17",' +
|
||||
'"Statement":{"Effect":"Allow""Action":"s3:PutObject",' +
|
||||
'"Resource":"arn:aws:s3*"}}');
|
||||
assert.deepStrictEqual(result, failRes(resource));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Version', () => {
|
||||
it('should validate with version date 2012-10-17', () => {
|
||||
check(policy, successRes);
|
||||
it('should validate user policy with version date 2012-10-17', () => {
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('should return error for other dates', () => {
|
||||
policy.Version = '2012-11-17';
|
||||
check(policy, failRes());
|
||||
it('should validate resource policy with version date 2012-10-17', () => {
|
||||
check(resourcePolicy, successRes, 'resource');
|
||||
});
|
||||
|
||||
it('should return error if Version field is missing', () => {
|
||||
policy.Version = undefined;
|
||||
check(policy, failRes(errDict.required.Version));
|
||||
it('user policy should return error for other dates', () => {
|
||||
userPolicy.Version = '2012-11-17';
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
|
||||
it('resource policy should return error for other dates', () => {
|
||||
resourcePolicy.Version = '2012-11-17';
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
});
|
||||
|
||||
it('should return error if Version field in user policy is missing', () => {
|
||||
userPolicy.Version = undefined;
|
||||
check(userPolicy, failRes(user, errDict.required.Version), user);
|
||||
});
|
||||
|
||||
it('should return error if Version field in resource policy is missing',
|
||||
() => {
|
||||
resourcePolicy.Version = undefined;
|
||||
check(resourcePolicy, failRes(resource, errDict.required.Version),
|
||||
resource);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -77,20 +130,24 @@ describe('Policies validation - Principal', () => {
|
|||
{
|
||||
name: 'an account id',
|
||||
value: { AWS: '111111111111' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'anonymous user AWS form',
|
||||
value: { AWS: '*' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'an account arn',
|
||||
value: { AWS: 'arn:aws:iam::111111111111:root' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple account id',
|
||||
value: {
|
||||
AWS: ['111111111111', '111111111112'],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple account arn',
|
||||
|
@ -100,14 +157,17 @@ describe('Policies validation - Principal', () => {
|
|||
'arn:aws:iam::111111111112:root',
|
||||
],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'anonymous user as string',
|
||||
value: '*',
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'user arn',
|
||||
value: { AWS: 'arn:aws:iam::111111111111:user/alex' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple user arns',
|
||||
|
@ -117,12 +177,14 @@ describe('Policies validation - Principal', () => {
|
|||
'arn:aws:iam::111111111111:user/thibault',
|
||||
],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'role arn',
|
||||
value: {
|
||||
AWS: 'arn:aws:iam::111111111111:role/dev',
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple role arn',
|
||||
|
@ -132,6 +194,7 @@ describe('Policies validation - Principal', () => {
|
|||
'arn:aws:iam::111111111111:role/prod',
|
||||
],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'saml provider',
|
||||
|
@ -139,57 +202,84 @@ describe('Policies validation - Principal', () => {
|
|||
Federated:
|
||||
'arn:aws:iam::111111111111:saml-provider/mysamlprovider',
|
||||
},
|
||||
policyType: [user],
|
||||
},
|
||||
{
|
||||
name: 'with backbeat service',
|
||||
value: { Service: 'backbeat' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'with canonical user id',
|
||||
value: { CanonicalUser:
|
||||
'1examplecanonicalid12345678909876' +
|
||||
'54321qwerty12345asdfg67890z1x2c' },
|
||||
policyType: [resource],
|
||||
},
|
||||
].forEach(test => {
|
||||
it(`should allow principal field with ${test.name}`, () => {
|
||||
policy.Statement.Principal = test.value;
|
||||
delete policy.Statement.Resource;
|
||||
check(policy, successRes);
|
||||
if (test.policyType.includes(user)) {
|
||||
it(`should allow user policy principal field with ${test.name}`,
|
||||
() => {
|
||||
userPolicy.Statement.Principal = test.value;
|
||||
delete userPolicy.Statement.Resource;
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it(`shoud allow notPrincipal field with ${test.name}`, () => {
|
||||
policy.Statement.NotPrincipal = test.value;
|
||||
delete policy.Statement.Resource;
|
||||
check(policy, successRes);
|
||||
it(`should allow user policy notPrincipal field with ${test.name}`,
|
||||
() => {
|
||||
userPolicy.Statement.NotPrincipal = test.value;
|
||||
delete userPolicy.Statement.Resource;
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
}
|
||||
if (test.policyType.includes(resource)) {
|
||||
it(`should allow resource policy principal field with ${test.name}`,
|
||||
() => {
|
||||
resourcePolicy.Statement[0].Principal = test.value;
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
[
|
||||
{
|
||||
name: 'wrong format account id',
|
||||
value: { AWS: '11111111111z' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'empty string',
|
||||
value: '',
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'anonymous user federated form',
|
||||
value: { federated: '*' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'wildcard in ressource',
|
||||
name: 'wildcard in resource',
|
||||
value: { AWS: 'arn:aws:iam::111111111111:user/*' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'a malformed account arn',
|
||||
value: { AWS: 'arn:aws:iam::111111111111:' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple malformed account id',
|
||||
value: {
|
||||
AWS: ['1111111111z1', '1111z1111112'],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple anonymous',
|
||||
value: {
|
||||
AWS: ['*', '*'],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple malformed account arn',
|
||||
|
@ -199,18 +289,22 @@ describe('Policies validation - Principal', () => {
|
|||
'arn:aws:iam::111111111112:',
|
||||
],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'account id as a string',
|
||||
value: '111111111111',
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'account arn as a string',
|
||||
value: 'arn:aws:iam::111111111111:root',
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'user arn as a string',
|
||||
value: 'arn:aws:iam::111111111111:user/alex',
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple malformed user arns',
|
||||
|
@ -220,12 +314,14 @@ describe('Policies validation - Principal', () => {
|
|||
'arn:aws:iam::111111111111:user/',
|
||||
],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'malformed role arn',
|
||||
value: {
|
||||
AWS: 'arn:aws:iam::111111111111:role/',
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'multiple malformed role arn',
|
||||
|
@ -235,36 +331,84 @@ describe('Policies validation - Principal', () => {
|
|||
'arn:aws:iam::11111111z111:role/prod',
|
||||
],
|
||||
},
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'saml provider as a string',
|
||||
value: 'arn:aws:iam::111111111111:saml-provider/mysamlprovider',
|
||||
policyType: [user],
|
||||
},
|
||||
{
|
||||
name: 'with other service than backbeat',
|
||||
value: { Service: 'non-existent-service' },
|
||||
policyType: [user, resource],
|
||||
},
|
||||
{
|
||||
name: 'invalid canonical user',
|
||||
value: { CanonicalUser:
|
||||
'12345invalid-canonical-id$$$//098' +
|
||||
'7654321poiu1q2w3e4r5t6y7u8i9o0p' },
|
||||
policyType: [resource],
|
||||
},
|
||||
].forEach(test => {
|
||||
it(`should fail with ${test.name}`, () => {
|
||||
policy.Statement.Principal = test.value;
|
||||
delete policy.Statement.Resource;
|
||||
check(policy, failRes());
|
||||
if (test.policyType.includes(user)) {
|
||||
it(`user policy should fail with ${test.name}`, () => {
|
||||
userPolicy.Statement.Principal = test.value;
|
||||
delete userPolicy.Statement.Resource;
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
}
|
||||
if (test.policyType.includes(resource)) {
|
||||
it(`resource policy should fail with ${test.name}`, () => {
|
||||
resourcePolicy.Statement[0].Principal = test.value;
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should not allow Resource field', () => {
|
||||
policy.Statement.Principal = '*';
|
||||
check(policy, failRes());
|
||||
userPolicy.Statement.Principal = '*';
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Statement', () => {
|
||||
it('should succeed for a valid object', () => {
|
||||
check(policy, successRes);
|
||||
[
|
||||
{
|
||||
name: 'should return error for undefined',
|
||||
value: undefined,
|
||||
},
|
||||
{
|
||||
name: 'should return an error for an empty list',
|
||||
value: [],
|
||||
},
|
||||
{
|
||||
name: 'should return an error for an empty object',
|
||||
value: {},
|
||||
errMessage: errDict.required.Action,
|
||||
},
|
||||
].forEach(test => {
|
||||
it(`user policy ${test.name}`, () => {
|
||||
userPolicy.Statement = test.value;
|
||||
check(userPolicy, failRes(user, test.errMessage), user);
|
||||
});
|
||||
|
||||
it('should succeed for a valid array', () => {
|
||||
policy.Statement = [
|
||||
it(`resource policy ${test.name}`, () => {
|
||||
resourcePolicy.Statement = test.value;
|
||||
check(resourcePolicy, failRes(resource, test.errMessage), resource);
|
||||
});
|
||||
});
|
||||
|
||||
it('user policy should succeed for a valid object', () => {
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('resource policy should succeed for a valid object', () => {
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
|
||||
it('user policy should succeed for a valid object', () => {
|
||||
userPolicy.Statement = [
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Action: 's3:PutObject',
|
||||
|
@ -276,255 +420,373 @@ describe('Policies validation - Statement', () => {
|
|||
Resource: 'arn:aws:s3:::my_bucket/uploads/widgetco/*',
|
||||
},
|
||||
];
|
||||
check(policy, successRes);
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('should return an error for undefined', () => {
|
||||
policy.Statement = undefined;
|
||||
check(policy, failRes());
|
||||
it('resource policy should succeed for a valid object', () => {
|
||||
resourcePolicy.Statement = [
|
||||
{
|
||||
Effect: 'Allow',
|
||||
Action: 's3:PutObject',
|
||||
Resource: 'arn:aws:s3:::my_bucket/uploads/widgetco/*',
|
||||
Principal: '*',
|
||||
},
|
||||
{
|
||||
Effect: 'Deny',
|
||||
Action: 's3:DeleteObject',
|
||||
Resource: 'arn:aws:s3:::my_bucket/uploads/widgetco/*',
|
||||
Principal: '*',
|
||||
},
|
||||
];
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
|
||||
it('should return an error for an empty list', () => {
|
||||
policy.Statement = [];
|
||||
check(policy, failRes());
|
||||
[
|
||||
{
|
||||
name: 'should return error for missing a required field - Action',
|
||||
toDelete: ['Action'],
|
||||
expected: 'fail',
|
||||
errMessage: errDict.required.Action,
|
||||
},
|
||||
{
|
||||
name: 'should return error for missing a required field - Effect',
|
||||
toDelete: ['Effect'],
|
||||
expected: 'fail',
|
||||
},
|
||||
{
|
||||
name: 'should return error for missing required field - Resource',
|
||||
toDelete: ['Resource'],
|
||||
expected: 'fail',
|
||||
},
|
||||
{
|
||||
name: 'should return error for missing multiple required fields',
|
||||
toDelete: ['Effect', 'Resource'],
|
||||
expected: 'fail',
|
||||
},
|
||||
{
|
||||
name: 'should succeed w optional fields missing - Sid, Condition',
|
||||
toDelete: ['Sid', 'Condition'],
|
||||
expected: successRes,
|
||||
},
|
||||
].forEach(test => {
|
||||
it(`user policy ${test.name}`, () => {
|
||||
test.toDelete.forEach(p => delete userPolicy.Statement[p]);
|
||||
if (test.expected === 'fail') {
|
||||
check(userPolicy, failRes(user, test.errMessage), user);
|
||||
} else {
|
||||
check(userPolicy, test.expected, user);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return an error for an empty object', () => {
|
||||
policy.Statement = {};
|
||||
check(policy, failRes(errDict.required.Action));
|
||||
it(`resource policy ${test.name}`, () => {
|
||||
test.toDelete.forEach(p => delete resourcePolicy.Statement[0][p]);
|
||||
if (test.expected === 'fail') {
|
||||
check(resourcePolicy, failRes(resource, test.errMessage),
|
||||
resource);
|
||||
} else {
|
||||
check(resourcePolicy, test.expected, resource);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return an error for missing a required field - Action', () => {
|
||||
delete policy.Statement.Action;
|
||||
check(policy, failRes(errDict.required.Action));
|
||||
});
|
||||
|
||||
it('should return an error for missing a required field - Effect', () => {
|
||||
delete policy.Statement.Effect;
|
||||
check(policy, failRes());
|
||||
});
|
||||
|
||||
it('should return an error for missing a required field - Resource', () => {
|
||||
delete policy.Statement.Resource;
|
||||
check(policy, failRes());
|
||||
});
|
||||
|
||||
it('should return an error for missing multiple required fields', () => {
|
||||
delete policy.Statement.Effect;
|
||||
delete policy.Statement.Resource;
|
||||
check(policy, failRes());
|
||||
});
|
||||
|
||||
it('should succeed with optional fields missing - Sid, Condition', () => {
|
||||
delete policy.Statement.Sid;
|
||||
delete policy.Statement.Condition;
|
||||
check(policy, successRes);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Statement::Sid_block', () => {
|
||||
it('should succeed if Sid is any alphanumeric string', () => {
|
||||
check(policy, successRes);
|
||||
it('user policy should succeed if Sid is any alphanumeric string', () => {
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('should fail if Sid is not a valid format', () => {
|
||||
policy.Statement.Sid = 'foo bar()';
|
||||
check(policy, failRes());
|
||||
it('resource policy should succeed if Sid is any alphanumeric string',
|
||||
() => {
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
|
||||
it('should fail if Sid is not a string', () => {
|
||||
policy.Statement.Sid = 1234;
|
||||
check(policy, failRes());
|
||||
it('user policy should fail if Sid is not a valid format', () => {
|
||||
userPolicy.Statement.Sid = 'foo bar()';
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
|
||||
it('resource policy should fail if Sid is not a valid format', () => {
|
||||
resourcePolicy.Statement[0].Sid = 'foo bar()';
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
});
|
||||
|
||||
it('user policy should fail if Sid is not a string', () => {
|
||||
userPolicy.Statement.Sid = 1234;
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
|
||||
it('resource policy should fail if Sid is not a string', () => {
|
||||
resourcePolicy.Statement[0].Sid = 1234;
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Statement::Effect_block', () => {
|
||||
it('should succeed for Allow', () => {
|
||||
check(policy, successRes);
|
||||
it('user policy should succeed for Allow', () => {
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('should succeed for Deny', () => {
|
||||
policy.Statement.Effect = 'Deny';
|
||||
check(policy, successRes);
|
||||
it('resource policy should succeed for Allow', () => {
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
|
||||
it('should fail for strings other than Allow/Deny', () => {
|
||||
policy.Statement.Effect = 'Reject';
|
||||
check(policy, failRes());
|
||||
it('user policy should succeed for Deny', () => {
|
||||
userPolicy.Statement.Effect = 'Deny';
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('should fail if Effect is not a string', () => {
|
||||
policy.Statement.Effect = 1;
|
||||
check(policy, failRes());
|
||||
it('resource policy should succeed for Deny', () => {
|
||||
resourcePolicy.Statement[0].Effect = 'Deny';
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
|
||||
it('user policy should fail for strings other than Allow/Deny', () => {
|
||||
userPolicy.Statement.Effect = 'Reject';
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
|
||||
it('resource policy should fail for strings other than Allow/Deny', () => {
|
||||
resourcePolicy.Statement[0].Effect = 'Reject';
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
});
|
||||
|
||||
it('user policy should fail if Effect is not a string', () => {
|
||||
userPolicy.Statement.Effect = 1;
|
||||
check(userPolicy, failRes(user), user);
|
||||
});
|
||||
|
||||
it('resource policy should fail if Effect is not a string', () => {
|
||||
resourcePolicy.Statement[0].Effect = 1;
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Statement::Action_block/' +
|
||||
const actionTests = [
|
||||
{
|
||||
name: 'should succeed for foo:bar',
|
||||
value: 'foo:bar',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should succeed for foo:*',
|
||||
value: 'foo:*',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should succeed for *',
|
||||
value: '*',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should fail for **',
|
||||
value: '**',
|
||||
expected: 'fail',
|
||||
errMessage: errDict.pattern.Action,
|
||||
},
|
||||
{
|
||||
name: 'should fail for foobar',
|
||||
value: 'foobar',
|
||||
expected: 'fail',
|
||||
errMessage: errDict.pattern.Action,
|
||||
},
|
||||
];
|
||||
|
||||
describe('User policies validation - Statement::Action_block/' +
|
||||
'Statement::NotAction_block', () => {
|
||||
beforeEach(() => {
|
||||
policy.Statement.Action = undefined;
|
||||
policy.Statement.NotAction = undefined;
|
||||
userPolicy.Statement.Action = undefined;
|
||||
userPolicy.Statement.NotAction = undefined;
|
||||
});
|
||||
|
||||
it('should succeed for foo:bar', () => {
|
||||
policy.Statement.Action = 'foo:bar';
|
||||
check(policy, successRes);
|
||||
actionTests.forEach(test => {
|
||||
it(`${test.name}`, () => {
|
||||
userPolicy.Statement.Action = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(userPolicy, failRes(user, test.errMessage), user);
|
||||
} else {
|
||||
check(userPolicy, test.expected, user);
|
||||
}
|
||||
|
||||
policy.Statement.Action = undefined;
|
||||
policy.Statement.NotAction = 'foo:bar';
|
||||
check(policy, successRes);
|
||||
userPolicy.Statement.Action = undefined;
|
||||
userPolicy.Statement.NotAction = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(userPolicy, failRes(user, test.errMessage), user);
|
||||
} else {
|
||||
check(userPolicy, test.expected, user);
|
||||
}
|
||||
});
|
||||
|
||||
it('should succeed for foo:*', () => {
|
||||
policy.Statement.Action = 'foo:*';
|
||||
check(policy, successRes);
|
||||
|
||||
policy.Statement.Action = undefined;
|
||||
policy.Statement.NotAction = 'foo:*';
|
||||
check(policy, successRes);
|
||||
});
|
||||
|
||||
it('should succeed for *', () => {
|
||||
policy.Statement.Action = '*';
|
||||
check(policy, successRes);
|
||||
|
||||
policy.Statement.Action = undefined;
|
||||
policy.Statement.NotAction = '*';
|
||||
check(policy, successRes);
|
||||
});
|
||||
|
||||
it('should fail for **', () => {
|
||||
policy.Statement.Action = '**';
|
||||
check(policy, failRes(errDict.pattern.Action));
|
||||
|
||||
policy.Statement.Action = undefined;
|
||||
policy.Statement.NotAction = '**';
|
||||
check(policy, failRes(errDict.pattern.Action));
|
||||
});
|
||||
|
||||
it('should fail for foobar', () => {
|
||||
policy.Statement.Action = 'foobar';
|
||||
check(policy, failRes(errDict.pattern.Action));
|
||||
|
||||
policy.Statement.Action = undefined;
|
||||
policy.Statement.NotAction = 'foobar';
|
||||
check(policy, failRes(errDict.pattern.Action));
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Statement::Resource_block' +
|
||||
describe('Resource policies validation - Statement::Action_block', () => {
|
||||
actionTests.forEach(test => {
|
||||
it(`${test.name}`, () => {
|
||||
resourcePolicy.Statement[0].Action = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(resourcePolicy, failRes(resource, test.errMessage),
|
||||
resource);
|
||||
} else {
|
||||
check(resourcePolicy, test.expected, resource);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const resourceTests = [
|
||||
{
|
||||
name: 'should succeed for arn:aws::s3:::*',
|
||||
value: 'arn:aws:s3:::*',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should succeed for arn:aws:s3:::test/home/${aws:username}',
|
||||
value: 'arn:aws:s3:::test/home/${aws:username}',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should succeed for arn:aws:ec2:us-west-1:1234567890:vol/*',
|
||||
value: 'arn:aws:ec2:us-west-1:1234567890:vol/*',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should succeed for *',
|
||||
value: '*',
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should fail for arn:aws:ec2:us-west-1:vol/* - missing region',
|
||||
value: 'arn:aws:ec2:us-west-1:vol/*',
|
||||
expected: 'fail',
|
||||
errMessage: errDict.pattern.Resource,
|
||||
},
|
||||
{
|
||||
name: 'should fail for arn:aws:ec2:us-west-1:123456789:v/${} - ${}',
|
||||
value: 'arn:aws:ec2:us-west-1:123456789:v/${}',
|
||||
expected: 'fail',
|
||||
errMessage: errDict.pattern.Resource,
|
||||
},
|
||||
{
|
||||
name: 'should fail for ec2:us-west-1:qwerty:vol/* - missing arn:aws:',
|
||||
value: 'ec2:us-west-1:123456789012:vol/*',
|
||||
expected: 'fail',
|
||||
errMessage: errDict.pattern.Resource,
|
||||
},
|
||||
];
|
||||
|
||||
describe('User policies validation - Statement::Resource_block' +
|
||||
'Statement::NotResource_block', () => {
|
||||
beforeEach(() => {
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = undefined;
|
||||
userPolicy.Statement.Resource = undefined;
|
||||
userPolicy.Statement.NotResource = undefined;
|
||||
});
|
||||
|
||||
it('should succeed for arn:aws:s3:::*', () => {
|
||||
policy.Statement.Resource = 'arn:aws:s3:::*';
|
||||
check(policy, successRes);
|
||||
resourceTests.forEach(test => {
|
||||
it(`${test.name}`, () => {
|
||||
userPolicy.Statement.Resource = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(userPolicy, failRes(user, test.errMessage), user);
|
||||
} else {
|
||||
check(userPolicy, test.expected, user);
|
||||
}
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = 'arn:aws:s3:::*';
|
||||
check(policy, successRes);
|
||||
userPolicy.Statement.Resource = undefined;
|
||||
userPolicy.Statement.NotResource = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(userPolicy, failRes(user, test.errMessage), user);
|
||||
} else {
|
||||
check(userPolicy, test.expected, user);
|
||||
}
|
||||
});
|
||||
|
||||
it('should succeed for arn:aws:s3:::test/home/${aws:username}', () => {
|
||||
policy.Statement.Resource = 'arn:aws:s3:::test/home/${aws:username}';
|
||||
check(policy, successRes);
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = 'arn:aws:s3:::test/home/${aws:username}';
|
||||
check(policy, successRes);
|
||||
});
|
||||
|
||||
it('should succeed for arn:aws:ec2:us-west-1:1234567890:vol/*', () => {
|
||||
policy.Statement.Resource = 'arn:aws:ec2:us-west-1:1234567890:vol/*';
|
||||
check(policy, successRes);
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = 'arn:aws:ec2:us-west-1:1234567890:vol/*';
|
||||
check(policy, successRes);
|
||||
});
|
||||
|
||||
it('should succeed for *', () => {
|
||||
policy.Statement.Resource = '*';
|
||||
check(policy, successRes);
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = '*';
|
||||
check(policy, successRes);
|
||||
});
|
||||
|
||||
it('should fail for arn:aws:ec2:us-west-1:vol/* - missing region', () => {
|
||||
policy.Statement.Resource = 'arn:aws:ec2:1234567890:vol/*';
|
||||
check(policy, failRes(errDict.pattern.Resource));
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = 'arn:aws:ec2:1234567890:vol/*';
|
||||
check(policy, failRes(errDict.pattern.Resource));
|
||||
});
|
||||
|
||||
it('should fail for arn:aws:ec2:us-west-1:123456789:v/${} - ${}', () => {
|
||||
policy.Statement.Resource = 'arn:aws:ec2:us-west-1:123456789:v/${}';
|
||||
check(policy, failRes(errDict.pattern.Resource));
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = 'arn:aws:ec2:us-west-1:123456789:v/${}';
|
||||
check(policy, failRes(errDict.pattern.Resource));
|
||||
});
|
||||
|
||||
it('should fail for ec2:us-west-1:qwerty:vol/* - missing arn:aws:', () => {
|
||||
policy.Statement.Resource = 'ec2:us-west-1:123456789012:vol/*';
|
||||
check(policy, failRes(errDict.pattern.Resource));
|
||||
|
||||
policy.Statement.Resource = undefined;
|
||||
policy.Statement.NotResource = 'ec2:us-west-1:123456789012:vol/*';
|
||||
check(policy, failRes(errDict.pattern.Resource));
|
||||
});
|
||||
|
||||
it('should fail for empty list of resources', () => {
|
||||
policy.Statement.Resource = [];
|
||||
check(policy, failRes(errDict.minItems.Resource));
|
||||
userPolicy.Statement.Resource = [];
|
||||
check(userPolicy, failRes(user, errDict.minItems.Resource), user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Resource policies validation - Statement::Resource_block', () => {
|
||||
resourceTests.forEach(test => {
|
||||
it(`${test.name}`, () => {
|
||||
resourcePolicy.Statement[0].Resource = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(resourcePolicy, failRes(resource, test.errMessage),
|
||||
resource);
|
||||
} else {
|
||||
check(resourcePolicy, test.expected, resource);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail for empty list of resources', () => {
|
||||
resourcePolicy.Statement[0].Resource = [];
|
||||
check(resourcePolicy, failRes(resource, errDict.minItems.Resource),
|
||||
resource);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Policies validation - Statement::Condition_block', () => {
|
||||
it('should succeed for single Condition', () => {
|
||||
check(policy, successRes);
|
||||
it('user policy should succeed for single Condition', () => {
|
||||
check(userPolicy, successRes, user);
|
||||
});
|
||||
|
||||
it('should succeed for multiple Conditions', () => {
|
||||
policy.Statement.Condition = {
|
||||
it('resource policy should succeed for single Condition', () => {
|
||||
check(resourcePolicy, successRes, resource);
|
||||
});
|
||||
|
||||
[
|
||||
{
|
||||
name: 'should succeed for multiple Conditions',
|
||||
value: {
|
||||
StringNotLike: { 's3:prefix': ['Development/*'] },
|
||||
Null: { 's3:prefix': false },
|
||||
};
|
||||
check(policy, successRes);
|
||||
});
|
||||
|
||||
it('should fail when Condition is not an Object', () => {
|
||||
policy.Statement.Condition = 'NumericLessThanEquals';
|
||||
check(policy, failRes());
|
||||
});
|
||||
|
||||
it('should fail for an invalid Condition', () => {
|
||||
policy.Statement.Condition = {
|
||||
},
|
||||
expected: successRes,
|
||||
},
|
||||
{
|
||||
name: 'should fail when Condition is not an Object',
|
||||
value: 'NumericLessThanEquals',
|
||||
expected: 'fail',
|
||||
},
|
||||
{
|
||||
name: 'should fail for an invalid Condition',
|
||||
value: {
|
||||
SomethingLike: { 's3:prefix': ['Development/*'] },
|
||||
};
|
||||
check(policy, failRes());
|
||||
});
|
||||
|
||||
it('should fail when one of the multiple conditions is invalid', () => {
|
||||
policy.Statement.Condition = {
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
{
|
||||
name: 'should fail when one of the multiple conditions is invalid',
|
||||
value: {
|
||||
Null: { 's3:prefix': false },
|
||||
SomethingLike: { 's3:prefix': ['Development/*'] },
|
||||
};
|
||||
check(policy, failRes());
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
{
|
||||
name: 'should fail when invalid property is assigned',
|
||||
value: {
|
||||
SomethingLike: { 's3:prefix': ['Development/*'] },
|
||||
},
|
||||
expected: 'fail',
|
||||
},
|
||||
].forEach(test => {
|
||||
it(`user policy ${test.name}`, () => {
|
||||
userPolicy.Statement.Condition = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(userPolicy, failRes(user), user);
|
||||
} else {
|
||||
check(userPolicy, test.expected, user);
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail when invalid property is assigned', () => {
|
||||
policy.Condition = {
|
||||
SomethingLike: { 's3:prefix': ['Development/*'] },
|
||||
};
|
||||
check(policy, failRes());
|
||||
it(`resource policy ${test.name}`, () => {
|
||||
resourcePolicy.Statement[0].Condition = test.value;
|
||||
if (test.expected === 'fail') {
|
||||
check(resourcePolicy, failRes(resource), resource);
|
||||
} else {
|
||||
check(resourcePolicy, test.expected, resource);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue