Skip to content

Commit 8e28237

Browse files
authored
Merge branch 'main' into nodejs-logging-migration
2 parents 8857306 + ad9ac5b commit 8e28237

3 files changed

Lines changed: 250 additions & 0 deletions

File tree

handwritten/storage/src/bucket.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ export interface RestoreOptions {
297297
generation: string;
298298
projection?: 'full' | 'noAcl';
299299
}
300+
export interface EncryptionEnforcementConfig {
301+
restrictionMode?: 'NotRestricted' | 'FullyRestricted';
302+
readonly effectiveTime?: string;
303+
}
300304
export interface BucketMetadata extends BaseMetadata {
301305
acl?: AclMetadata[] | null;
302306
autoclass?: {
@@ -316,6 +320,9 @@ export interface BucketMetadata extends BaseMetadata {
316320
defaultObjectAcl?: AclMetadata[];
317321
encryption?: {
318322
defaultKmsKeyName?: string;
323+
googleManagedEncryptionEnforcementConfig?: EncryptionEnforcementConfig;
324+
customerManagedEncryptionEnforcementConfig?: EncryptionEnforcementConfig;
325+
customerSuppliedEncryptionEnforcementConfig?: EncryptionEnforcementConfig;
319326
} | null;
320327
hierarchicalNamespace?: {
321328
enabled?: boolean;
@@ -1193,6 +1200,25 @@ class Bucket extends ServiceObject<Bucket, BucketMetadata> {
11931200
* }, function(err, apiResponse) {});
11941201
*
11951202
* //-
1203+
* // Enforce CMEK-only encryption for new objects.
1204+
* // This blocks Google-Managed and Customer-Supplied keys.
1205+
* //-
1206+
* bucket.setMetadata({
1207+
* encryption: {
1208+
* defaultKmsKeyName: 'projects/grape-spaceship-123/...',
1209+
* googleManagedEncryptionEnforcementConfig: {
1210+
* restrictionMode: 'FullyRestricted'
1211+
* },
1212+
* customerSuppliedEncryptionEnforcementConfig: {
1213+
* restrictionMode: 'FullyRestricted'
1214+
* },
1215+
* customerManagedEncryptionEnforcementConfig: {
1216+
* restrictionMode: 'NotRestricted'
1217+
* }
1218+
* }
1219+
* }, function(err, apiResponse) {});
1220+
*
1221+
* //-
11961222
* // Set the default event-based hold value for new objects in this
11971223
* // bucket.
11981224
* //-

handwritten/storage/system-test/storage.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2992,6 +2992,89 @@ describe('storage', function () {
29922992
`${metadata!.encryption!.defaultKmsKeyName}/cryptoKeyVersions/1`,
29932993
);
29942994
});
2995+
2996+
describe('encryption enforcement', () => {
2997+
it('should enforce FullyRestricted CSEK policy', async () => {
2998+
await bucket.setMetadata({
2999+
encryption: {
3000+
defaultKmsKeyName: kmsKeyName,
3001+
customerSuppliedEncryptionEnforcementConfig: {
3002+
restrictionMode: 'FullyRestricted',
3003+
},
3004+
},
3005+
});
3006+
3007+
await new Promise(res =>
3008+
setTimeout(res, BUCKET_METADATA_UPDATE_WAIT_TIME)
3009+
);
3010+
3011+
const encryptionKey = crypto.randomBytes(32);
3012+
const file = bucket.file('csek-attempt', {encryptionKey});
3013+
3014+
await assert.rejects(
3015+
file.save(FILE_CONTENTS, {resumable: false}),
3016+
(err: ApiError) => {
3017+
const failureMessage =
3018+
"Requested encryption type for object is not compliant with the bucket's encryption enforcement configuration.";
3019+
assert.strictEqual(err.code, 412);
3020+
assert.ok(err.message.includes(failureMessage));
3021+
return true;
3022+
}
3023+
);
3024+
});
3025+
3026+
it('should allow uploads that comply with enforcement', async () => {
3027+
await bucket.setMetadata({
3028+
encryption: {
3029+
googleManagedEncryptionEnforcementConfig: {
3030+
restrictionMode: 'NotRestricted',
3031+
},
3032+
},
3033+
});
3034+
3035+
const file = bucket.file('compliant-file');
3036+
await file.save(FILE_CONTENTS);
3037+
3038+
const [metadata] = await file.getMetadata();
3039+
assert.ok(metadata.customerEncryption);
3040+
});
3041+
3042+
it('should retain defaultKmsKeyName when updating enforcement settings independently', async () => {
3043+
await bucket.setMetadata({
3044+
encryption: {
3045+
defaultKmsKeyName: kmsKeyName,
3046+
},
3047+
});
3048+
3049+
await new Promise(res =>
3050+
setTimeout(res, BUCKET_METADATA_UPDATE_WAIT_TIME)
3051+
);
3052+
3053+
await bucket.setMetadata({
3054+
encryption: {
3055+
googleManagedEncryptionEnforcementConfig: {
3056+
restrictionMode: 'FullyRestricted',
3057+
},
3058+
},
3059+
});
3060+
3061+
await new Promise(res =>
3062+
setTimeout(res, BUCKET_METADATA_UPDATE_WAIT_TIME)
3063+
);
3064+
3065+
const [metadata] = await bucket.getMetadata();
3066+
assert.strictEqual(
3067+
metadata.encryption?.defaultKmsKeyName,
3068+
kmsKeyName
3069+
);
3070+
3071+
assert.strictEqual(
3072+
metadata.encryption?.googleManagedEncryptionEnforcementConfig
3073+
?.restrictionMode,
3074+
'FullyRestricted'
3075+
);
3076+
});
3077+
});
29953078
});
29963079
});
29973080

handwritten/storage/test/bucket.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3300,4 +3300,145 @@ describe('Bucket', () => {
33003300
done();
33013301
});
33023302
});
3303+
3304+
describe('setMetadata', () => {
3305+
describe('encryption enforcement', () => {
3306+
it('should correctly format restrictionMode for all enforcement types', () => {
3307+
const effectiveTime = '2026-02-02T12:00:00Z';
3308+
const encryptionMetadata = {
3309+
encryption: {
3310+
defaultKmsKeyName: 'kms-key-name',
3311+
googleManagedEncryptionEnforcementConfig: {
3312+
restrictionMode: 'FullyRestricted',
3313+
effectiveTime: effectiveTime,
3314+
},
3315+
customerManagedEncryptionEnforcementConfig: {
3316+
restrictionMode: 'NotRestricted',
3317+
effectiveTime: effectiveTime,
3318+
},
3319+
customerSuppliedEncryptionEnforcementConfig: {
3320+
restrictionMode: 'FullyRestricted',
3321+
effectiveTime: effectiveTime,
3322+
},
3323+
},
3324+
};
3325+
3326+
bucket.setMetadata = (metadata: BucketMetadata) => {
3327+
assert.strictEqual(
3328+
metadata.encryption?.defaultKmsKeyName,
3329+
encryptionMetadata.encryption.defaultKmsKeyName
3330+
);
3331+
3332+
assert.deepStrictEqual(
3333+
metadata.encryption?.googleManagedEncryptionEnforcementConfig,
3334+
{restrictionMode: 'FullyRestricted', effectiveTime: effectiveTime}
3335+
);
3336+
3337+
assert.deepStrictEqual(
3338+
metadata.encryption?.customerManagedEncryptionEnforcementConfig,
3339+
{restrictionMode: 'NotRestricted', effectiveTime: effectiveTime}
3340+
);
3341+
3342+
assert.deepStrictEqual(
3343+
metadata.encryption?.customerSuppliedEncryptionEnforcementConfig,
3344+
{restrictionMode: 'FullyRestricted', effectiveTime: effectiveTime}
3345+
);
3346+
};
3347+
bucket.setMetadata(encryptionMetadata, assert.ifError);
3348+
});
3349+
3350+
it('should preserve existing encryption fields during a partial update', done => {
3351+
bucket.metadata = {
3352+
encryption: {
3353+
defaultKmsKeyName: 'kms-key-name',
3354+
googleManagedEncryptionEnforcementConfig: {
3355+
restrictionMode: 'FullyRestricted',
3356+
},
3357+
},
3358+
};
3359+
3360+
const patch = {
3361+
encryption: {
3362+
customerSuppliedEncryptionEnforcementConfig: {
3363+
restrictionMode: 'FullyRestricted',
3364+
},
3365+
},
3366+
};
3367+
3368+
bucket.setMetadata = (metadata: BucketMetadata) => {
3369+
assert.strictEqual(
3370+
metadata.encryption?.customerSuppliedEncryptionEnforcementConfig
3371+
?.restrictionMode,
3372+
'FullyRestricted'
3373+
);
3374+
done();
3375+
};
3376+
3377+
bucket.setMetadata(patch, assert.ifError);
3378+
});
3379+
3380+
it('should reject or handle invalid restrictionMode values', done => {
3381+
const invalidMetadata = {
3382+
encryption: {
3383+
googleManagedEncryptionEnforcementConfig: {
3384+
restrictionMode: 'fully_restricted',
3385+
},
3386+
},
3387+
};
3388+
3389+
bucket.setMetadata = (metadata: BucketMetadata) => {
3390+
assert.strictEqual(
3391+
metadata.encryption?.googleManagedEncryptionEnforcementConfig
3392+
?.restrictionMode,
3393+
'fully_restricted'
3394+
);
3395+
done();
3396+
};
3397+
3398+
bucket.setMetadata(invalidMetadata, assert.ifError);
3399+
});
3400+
3401+
it('should not include enforcement configs that are not provided', done => {
3402+
const partialMetadata = {
3403+
encryption: {
3404+
defaultKmsKeyName: 'test-key',
3405+
googleManagedEncryptionEnforcementConfig: {
3406+
restrictionMode: 'FullyRestricted',
3407+
},
3408+
},
3409+
};
3410+
3411+
bucket.setMetadata = (metadata: BucketMetadata) => {
3412+
assert.ok(metadata.encryption?.defaultKmsKeyName);
3413+
assert.ok(
3414+
metadata.encryption?.googleManagedEncryptionEnforcementConfig
3415+
);
3416+
assert.strictEqual(
3417+
metadata.encryption?.customerManagedEncryptionEnforcementConfig,
3418+
undefined
3419+
);
3420+
assert.strictEqual(
3421+
metadata.encryption?.customerSuppliedEncryptionEnforcementConfig,
3422+
undefined
3423+
);
3424+
done();
3425+
};
3426+
3427+
bucket.setMetadata(partialMetadata, assert.ifError);
3428+
});
3429+
3430+
it('should allow nullifying encryption enforcement', done => {
3431+
const clearMetadata = {
3432+
encryption: null,
3433+
};
3434+
3435+
bucket.setMetadata = (metadata: BucketMetadata) => {
3436+
assert.strictEqual(metadata.encryption, null);
3437+
done();
3438+
};
3439+
3440+
bucket.setMetadata(clearMetadata, assert.ifError);
3441+
});
3442+
});
3443+
});
33033444
});

0 commit comments

Comments
 (0)