Skip to content

aws-ec2: invalid or missing CIDR mask reports "NaN.NaN.NaN.NaN is not a valid IP Address", and /33 silently produces a wrong block #38523

Description

@Adityaj0

Describe the bug

An invalid or missing CIDR mask is never validated, so it flows into the address arithmetic in CidrBlock and surfaces as an error naming an IP address the caller never wrote — or, for a mask above 32, produces a nonsensical block with no error at all.

Forgetting the mask is an easy mistake, and the resulting message gives no hint that the mask is the problem:

ec2.IpAddresses.cidr('10.0.0.0')
  -> Error: NaN.NaN.NaN.NaN is not a valid IP Address

Nothing in that message points at the missing /16.

Full behaviour of IpAddresses.cidr() on aws-cdk-lib 2.263.0:

input result
'10.0.0.0' (no mask) NaN.NaN.NaN.NaN is not a valid IP Address
'10.0.0.0/' NaN.NaN.NaN.NaN is not a valid IP Address
'10.0.0.0/abc' NaN.NaN.NaN.NaN is not a valid IP Address
'10.0.0.0/-1' 512.0.0.0 is not a valid IP Address
'10.0.0.0/33' ⚠️ no error — silently becomes 9.255.255.255/33

The /33 row is the worst of these: new CidrBlock('10.0.0.0/33') returns a block whose cidr is 9.255.255.255/33, an address outside the range the caller asked for, and synthesis continues. The failure only appears later — either as a confusing downstream error (1 of /24 exceeds remaining space of 9.255.255.255/33) or at deploy time from CloudFormation.

Expected Behavior

An invalid mask should fail fast with an error that names the mask as the problem and states the accepted range, per the repo's error-message guidance (include the wrong value, the expected values, and what to change). Something like:

invalid CIDR mask in "10.0.0.0", expected an integer between 0 and 32 after a '/', e.g. '10.0.0.0/16'

Current Behavior

The mask is read with parseInt and used without any validation:

// packages/aws-cdk-lib/aws-ec2/lib/network-util.ts
this.mask = parseInt(ipAddressOrCidr.split('/')[1], 10);
this.networkAddress = NetworkUtils.ipToNum(ipAddressOrCidr.split('/')[0]) +
  CidrBlock.calculateNetsize(this.mask) - 1;

With no mask, parseInt(undefined, 10) is NaN, so calculateNetsize returns 2 ** (32 - NaN) = NaN, networkAddress becomes NaN, and numToIp finally reports NaN.NaN.NaN.NaN. With /33, calculateNetsize returns 0.5, and the block silently resolves one address below the input.

Reproduction Steps

import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

const app = new cdk.App();
const stack = new cdk.Stack(app, 'S');

// Forgot the mask
new ec2.Vpc(stack, 'V', {
  ipAddresses: ec2.IpAddresses.cidr('10.0.0.0'),   // -> NaN.NaN.NaN.NaN is not a valid IP Address
});

Or, isolating the mask handling with no CDK app at all — this is the exact arithmetic from network-util.ts:

const mask = parseInt('10.0.0.0'.split('/')[1], 10);  // NaN
const netsize = 2 ** (32 - mask);                     // NaN
// -> networkAddress NaN -> numToIp(NaN) -> 'NaN.NaN.NaN.NaN'

const mask33 = parseInt('10.0.0.0/33'.split('/')[1], 10);  // 33
const netsize33 = 2 ** (32 - mask33);                      // 0.5  <-- half an address

Possible Solution

Validate the mask in the CidrBlock constructor before it reaches the arithmetic, and throw an UnscopedValidationError naming the mask.

Keeping parseInt semantics for the parse itself matters for backwards compatibility: '10.0.0.0/16abc' and '10.0.0.0/16.5' resolve to 16 today and synthesize successfully, so they must keep working. Only masks that cannot produce a usable block (NaN, < 0, > 32) should be rejected. Under that rule no input that synthesizes today changes behaviour:

input before after
10.0.0.0/16 10.0.0.0/16 10.0.0.0/16
0.0.0.0/0 0.0.0.0/0 0.0.0.0/0
10.0.0.1/32 10.0.0.1/32 10.0.0.1/32
10.0.0.0/16abc 10.0.0.0/16 10.0.0.0/16
10.0.0.0/16.5 10.0.0.0/16 10.0.0.0/16
10.0.0.0/33 9.255.255.255/33 clear error
10.0.0.0/-1 512.0.0.0 is not a valid IP Address clear error
10.0.0.0 NaN.NaN.NaN.NaN is not a valid IP Address clear error

The only rows that change are ones that already failed, or that produced a block CloudFormation would reject — so no feature flag is needed under the contributing guidance for validation changes.

Other Information

Related but distinct: #34784 covers misaligned base addresses being silently rounded up (10.0.3.1/2810.0.3.16/28). This issue is about the mask, and is unaffected by that one.

Separately, and not proposed for change here: the octet check in NetworkUtils.validIp uses parseInt per octet, so '1.2.3.1e2/16' is accepted and synthesizes a VPC with CidrBlock 1.3.0.0/16. Tightening that would reject input that currently deploys, so it would need a feature flag — I've left it out to keep this to one concern.

CDK CLI Version

N/A (synthesis-time, framework only)

Framework Version

aws-cdk-lib 2.263.0

Node.js Version

v24.1.0

OS

macOS 15 (Darwin 25.2.0)

Language

TypeScript

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-ec2Related to Amazon Elastic Compute CloudbugThis issue is a bug.effort/smallSmall work item – less than a day of effortp2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions