@kensio/yulin 1.21.31 → 1.21.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/serve/message/sim-message-log-console.js +3 -3
- package/dist/service/apigateway/api/deployment/sim-rest-api-deployment.js +3 -3
- package/dist/service/apigateway/api/method/sim-rest-api-method.js +12 -12
- package/dist/service/apigateway/api/resource/sim-rest-api-resource.js +11 -9
- package/dist/service/apigateway/api/sim-rest-api-view.js +3 -3
- package/dist/service/apigateway/api/stage/sim-rest-api-stage.js +8 -10
- package/dist/service/apigatewayv2/api/integration/sim-http-api-integration.js +3 -3
- package/dist/service/apigatewayv2/api/route/sim-http-api-route-view.js +6 -6
- package/dist/service/apigatewayv2/api/sim-http-api-view.js +3 -3
- package/dist/service/apigatewayv2/api/stage/settings/sim-http-api-stage-route-settings.js +8 -7
- package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.js +9 -9
- package/dist/service/athena/engine/sim-athena-partitioned-objects.js +7 -7
- package/dist/service/athena/sim-athena-engine.fixture.js +3 -0
- package/dist/service/athena/table/sim-athena-partition-filters.js +6 -5
- package/dist/service/backup/command/sim-backup-authorizer.js +5 -4
- package/dist/service/bedrock/response/sim-bedrock-declared-content.js +5 -10
- package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.js +19 -10
- package/dist/service/cloudfront/cfn/response-headers-policy/sim-cfn-cf-rh-policy-security-values.js +2 -6
- package/dist/service/cognito/user-pool/trigger/sim-cognito-pre-sign-up-response.js +4 -7
- package/dist/service/dynamodb/command/transact/sim-dynamodb-transact-write.js +34 -25
- package/dist/service/dynamodb/secondary-index/sim-dynamodb-index-limits.js +5 -4
- package/dist/service/dynamodb/stream/sim-dynamodb-stream-shard-iterator.js +1 -3
- package/dist/service/dynamodb/time-to-live/sim-dynamodb-time-to-live.js +4 -3
- package/dist/service/glue/expression/sim-glue-partition-comparisons.js +1 -1
- package/dist/service/iam/authorize/context/sim-iam-auth-z-allow-requirement.js +6 -6
- package/dist/service/iam/authorize/match/condition/null/sim-iam-null.js +4 -1
- package/dist/service/kinesis/stream/sim-kinesis-shard-iterator.js +1 -3
- package/dist/service/lambda/command/create-function/create-function.handler.js +1 -0
- package/dist/service/lambda/destination/sim-lambda-destination-record.js +7 -7
- package/dist/service/lambda/function/environment/sim-lambda-environment-conflict.d.ts +22 -0
- package/dist/service/lambda/function/environment/sim-lambda-environment-conflict.js +33 -0
- package/dist/service/lambda/function/environment/sim-lambda-environment-conflicts.d.ts +53 -11
- package/dist/service/lambda/function/environment/sim-lambda-environment-conflicts.js +78 -24
- package/dist/service/lambda/function/policy/sim-lambda-permission.js +15 -14
- package/dist/service/lambda/function/sim-lambda-function.js +1 -0
- package/dist/service/lambda/function/sim-lambda-function.type.d.ts +8 -0
- package/dist/service/lambda/metric/sim-lambda-function-metrics.js +11 -9
- package/dist/service/lambda/sim-lambda-commands.js +3 -1
- package/dist/service/logs/metric/sim-logs-metric-filter.js +1 -8
- package/dist/service/route53/dns/answer/sim-route53-dns-zone-finder.js +5 -4
- package/dist/service/route53/resolve/sim-r53-zone-record-finder.js +8 -6
- package/dist/service/s3/bucket/notification/sim-s3-notification-filter.js +4 -7
- package/dist/service/s3/bucket/sim-s3-bucket.d.ts +8 -0
- package/dist/service/s3/bucket/sim-s3-bucket.js +11 -0
- package/dist/service/s3/bucket/sim-s3-key-writes.d.ts +16 -0
- package/dist/service/s3/bucket/sim-s3-key-writes.js +43 -0
- package/dist/service/s3/bucket/versioning/sim-s3-bucket-versioning.js +1 -3
- package/dist/service/s3/command/complete-multipart-upload/complete-multipart-upload.command.d.ts +2 -1
- package/dist/service/s3/command/complete-multipart-upload/complete-multipart-upload.handler.js +5 -1
- package/dist/service/s3/command/copy-object/copy-object-precondition.d.ts +23 -0
- package/dist/service/s3/command/copy-object/copy-object-precondition.js +34 -0
- package/dist/service/s3/command/copy-object/copy-object.command.d.ts +3 -1
- package/dist/service/s3/command/copy-object/copy-object.handler.d.ts +4 -0
- package/dist/service/s3/command/copy-object/copy-object.handler.js +11 -1
- package/dist/service/s3/command/object/sim-s3-write-precondition.d.ts +34 -0
- package/dist/service/s3/command/object/sim-s3-write-precondition.js +47 -0
- package/dist/service/s3/command/put-object/put-object.command.d.ts +2 -1
- package/dist/service/s3/command/put-object/put-object.handler.js +5 -1
- package/dist/service/s3/error/sim-s3.error.d.ts +10 -0
- package/dist/service/s3/error/sim-s3.error.js +12 -0
- package/dist/service/s3/object/s3-object-listing.js +4 -3
- package/dist/service/s3/serve/api/sim-s3-api-input.d.ts +4 -0
- package/dist/service/s3/serve/api/sim-s3-api-input.js +10 -0
- package/dist/service/s3/serve/api/sim-s3-api-multipart-input.js +2 -0
- package/dist/service/s3/serve/api/sim-s3-api-object-input.d.ts +6 -0
- package/dist/service/s3/serve/api/sim-s3-api-object-input.js +34 -2
- package/dist/service/s3/storage/filesystem/s3-filesystem-object-keys.js +9 -8
- package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.js +15 -23
- package/dist/service/sqs/message/sim-sqs-message.js +3 -3
- package/dist/service/stepfunctions/machine/sim-state-machine.js +4 -3
- package/dist/util/process/sim-process-environment-reads.d.ts +52 -0
- package/dist/util/process/sim-process-environment-reads.js +94 -0
- package/dist/util/process/sim-process-environment.d.ts +32 -9
- package/dist/util/process/sim-process-environment.js +43 -17
- package/dist/util/schedule/cron/sim-cron-field-bounds.js +1 -1
- package/docs/services/lambda/README.md +6 -0
- package/docs/services/s3/README.md +84 -3
- package/package.json +3 -3
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SimS3Object } from "../../object/s3-object.js";
|
|
2
|
+
/**
|
|
3
|
+
* The conditions a copy can place on its source Object, as the
|
|
4
|
+
* `x-amz-copy-source-if-*` headers.
|
|
5
|
+
*/
|
|
6
|
+
export interface SimS3CopySourcePrecondition {
|
|
7
|
+
readonly CopySourceIfMatch?: string | undefined;
|
|
8
|
+
readonly CopySourceIfNoneMatch?: string | undefined;
|
|
9
|
+
readonly CopySourceIfModifiedSince?: Date | undefined;
|
|
10
|
+
readonly CopySourceIfUnmodifiedSince?: Date | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Refuse a copy whose source Object fails one of its conditions.
|
|
14
|
+
*
|
|
15
|
+
* Real S3 follows RFC 7232 here. An ETag condition takes precedence over the
|
|
16
|
+
* date condition paired with it, so a source that matches `if-match` is copied
|
|
17
|
+
* however recently it changed, and a source that fails `if-none-match` is
|
|
18
|
+
* refused however long ago it changed. HTTP dates carry whole seconds, and the
|
|
19
|
+
* Object's last-modified time is compared at that precision.
|
|
20
|
+
*
|
|
21
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html
|
|
22
|
+
*/
|
|
23
|
+
export declare function simS3RequireCopySourcePrecondition(source: SimS3Object, condition: SimS3CopySourcePrecondition): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SimS3PreconditionFailed } from "../../error/sim-s3.error.js";
|
|
2
|
+
import { simS3ETagMatches, simS3PreconditionMessage, } from "../object/sim-s3-write-precondition.js";
|
|
3
|
+
/**
|
|
4
|
+
* Refuse a copy whose source Object fails one of its conditions.
|
|
5
|
+
*
|
|
6
|
+
* Real S3 follows RFC 7232 here. An ETag condition takes precedence over the
|
|
7
|
+
* date condition paired with it, so a source that matches `if-match` is copied
|
|
8
|
+
* however recently it changed, and a source that fails `if-none-match` is
|
|
9
|
+
* refused however long ago it changed. HTTP dates carry whole seconds, and the
|
|
10
|
+
* Object's last-modified time is compared at that precision.
|
|
11
|
+
*
|
|
12
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html
|
|
13
|
+
*/
|
|
14
|
+
export function simS3RequireCopySourcePrecondition(source, condition) {
|
|
15
|
+
if (!sourceConditionsHold(source, condition)) {
|
|
16
|
+
throw new SimS3PreconditionFailed(simS3PreconditionMessage);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function sourceConditionsHold(source, condition) {
|
|
20
|
+
const modified = wholeSeconds(source.lastModified);
|
|
21
|
+
const { CopySourceIfMatch: ifMatch, CopySourceIfNoneMatch: ifNoneMatch, CopySourceIfModifiedSince: ifModifiedSince, CopySourceIfUnmodifiedSince: ifUnmodifiedSince, } = condition;
|
|
22
|
+
const matchHolds = ifMatch === undefined
|
|
23
|
+
? ifUnmodifiedSince === undefined ||
|
|
24
|
+
modified <= wholeSeconds(ifUnmodifiedSince)
|
|
25
|
+
: simS3ETagMatches(ifMatch, source.etag);
|
|
26
|
+
const noneMatchHolds = ifNoneMatch === undefined
|
|
27
|
+
? ifModifiedSince === undefined ||
|
|
28
|
+
modified > wholeSeconds(ifModifiedSince)
|
|
29
|
+
: !simS3ETagMatches(ifNoneMatch, source.etag);
|
|
30
|
+
return matchHolds && noneMatchHolds;
|
|
31
|
+
}
|
|
32
|
+
function wholeSeconds(date) {
|
|
33
|
+
return Math.floor(date.getTime() / 1000);
|
|
34
|
+
}
|
|
@@ -2,6 +2,8 @@ import type { SimResponseMetadata } from "../../../aws/metadata/response-metadat
|
|
|
2
2
|
import type { SimS3ObjectWriteMetadata } from "../../object/s3-write-metadata.js";
|
|
3
3
|
import type { SimS3ObjectWriteStorage } from "../../object/s3-write-storage.js";
|
|
4
4
|
import type { SimS3ObjectWriteTagging } from "../../object/s3-write-tagging.js";
|
|
5
|
+
import type { SimS3WritePrecondition } from "../object/sim-s3-write-precondition.js";
|
|
6
|
+
import type { SimS3CopySourcePrecondition } from "./copy-object-precondition.js";
|
|
5
7
|
/**
|
|
6
8
|
* Minimal structural sim S3 CopyObject command.
|
|
7
9
|
*/
|
|
@@ -15,7 +17,7 @@ export interface SimCopyObjectCommand {
|
|
|
15
17
|
* apply to the destination Object under `MetadataDirective: REPLACE`. Under
|
|
16
18
|
* the default `COPY` they are ignored, as real S3 ignores them.
|
|
17
19
|
*/
|
|
18
|
-
export interface SimCopyObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging {
|
|
20
|
+
export interface SimCopyObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging, SimS3WritePrecondition, SimS3CopySourcePrecondition {
|
|
19
21
|
readonly Bucket?: string | undefined;
|
|
20
22
|
readonly Key?: string | undefined;
|
|
21
23
|
/** The source Object, as `sourceBucket/sourceKey`, URL-encoded. */
|
|
@@ -36,6 +36,10 @@ export declare class CopyObjectCommandHandler implements CommandHandler<SimCopyO
|
|
|
36
36
|
* decision. That keeps a malformed request and a missing Bucket answering
|
|
37
37
|
* what real S3 answers. The source Object is read after authorization, which
|
|
38
38
|
* leaves a refused caller knowing nothing about which keys exist.
|
|
39
|
+
*
|
|
40
|
+
* The conditions on the source are checked once it has been read, and the
|
|
41
|
+
* conditions on the destination after that. Either one failing refuses the
|
|
42
|
+
* copy before anything is written.
|
|
39
43
|
*/
|
|
40
44
|
handle(command: SimCopyObjectCommand, options?: SimS3RequestOptions): Promise<SimCopyObjectCommandOutput>;
|
|
41
45
|
}
|
|
@@ -4,8 +4,10 @@ import { SimIamAllowAllAuth, } from "../../../iam/authorize/sim-iam-inter-servic
|
|
|
4
4
|
import { simS3QuotedETag } from "../../object/s3-object-etag.js";
|
|
5
5
|
import { GetObjectAuthorizer } from "../get-object/get-object-authorizer.js";
|
|
6
6
|
import { PutObjectAuthorizer } from "../put-object/put-object-authorizer.js";
|
|
7
|
+
import { simS3RequireWritePrecondition } from "../object/sim-s3-write-precondition.js";
|
|
7
8
|
import { requireSimS3Bucket } from "../require-sim-s3-bucket.js";
|
|
8
9
|
import { CopyObjectBuilder } from "./copy-object-builder.js";
|
|
10
|
+
import { simS3RequireCopySourcePrecondition } from "./copy-object-precondition.js";
|
|
9
11
|
import { simS3CopySource, simS3CopySourceObject, simS3RefuseUnchangedSelfCopy, } from "./copy-object-source.js";
|
|
10
12
|
/**
|
|
11
13
|
* Simulated S3 CopyObjectCommand handler.
|
|
@@ -40,6 +42,10 @@ export class CopyObjectCommandHandler {
|
|
|
40
42
|
* decision. That keeps a malformed request and a missing Bucket answering
|
|
41
43
|
* what real S3 answers. The source Object is read after authorization, which
|
|
42
44
|
* leaves a refused caller knowing nothing about which keys exist.
|
|
45
|
+
*
|
|
46
|
+
* The conditions on the source are checked once it has been read, and the
|
|
47
|
+
* conditions on the destination after that. Either one failing refuses the
|
|
48
|
+
* copy before anything is written.
|
|
43
49
|
*/
|
|
44
50
|
async handle(command, options) {
|
|
45
51
|
assertDefined(command.input.Bucket, "CopyObjectCommand.input.Bucket");
|
|
@@ -56,8 +62,12 @@ export class CopyObjectCommandHandler {
|
|
|
56
62
|
this.sourceAuthorizer.authorize(source, from.key, options);
|
|
57
63
|
const caller = this.destinationAuthorizer.authorize(destination, key, options);
|
|
58
64
|
const stored = await simS3CopySourceObject(source, from.key);
|
|
65
|
+
simS3RequireCopySourcePrecondition(stored, command.input);
|
|
59
66
|
const object = this.objectBuilder.build(command.input, key, stored, destination.getEncryption().algorithm);
|
|
60
|
-
const version = await destination.
|
|
67
|
+
const version = await destination.writeKey(key, async () => {
|
|
68
|
+
await simS3RequireWritePrecondition(destination, key, command.input);
|
|
69
|
+
return await destination.putObject(object);
|
|
70
|
+
});
|
|
61
71
|
this.notifications.objectCreated({
|
|
62
72
|
bucket: destination,
|
|
63
73
|
object,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { SimS3Bucket } from "../../bucket/sim-s3-bucket.js";
|
|
2
|
+
/**
|
|
3
|
+
* The conditions a write can place on the Object already under its key.
|
|
4
|
+
*
|
|
5
|
+
* PutObject, CopyObject and CompleteMultipartUpload all take these, as the
|
|
6
|
+
* `If-None-Match` and `If-Match` headers.
|
|
7
|
+
*
|
|
8
|
+
* https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-writes.html
|
|
9
|
+
*/
|
|
10
|
+
export interface SimS3WritePrecondition {
|
|
11
|
+
readonly IfMatch?: string | undefined;
|
|
12
|
+
readonly IfNoneMatch?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The message real S3 gives with `PreconditionFailed`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const simS3PreconditionMessage = "At least one of the pre-conditions you specified did not hold";
|
|
18
|
+
/**
|
|
19
|
+
* Refuse a write whose condition does not hold against the key's current
|
|
20
|
+
* Object.
|
|
21
|
+
*
|
|
22
|
+
* `If-None-Match: *` fails when the key holds an Object. `If-Match` fails when
|
|
23
|
+
* the Object's ETag differs, and answers `NoSuchKey` when the key holds
|
|
24
|
+
* nothing. A delete marker counts as nothing, because a read of the key finds
|
|
25
|
+
* nothing behind it. Real S3 accepts only `*` for `If-None-Match` on a write.
|
|
26
|
+
*/
|
|
27
|
+
export declare function simS3RequireWritePrecondition(bucket: SimS3Bucket, key: string, condition: SimS3WritePrecondition): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Whether an entity tag a client sent names an Object's ETag.
|
|
30
|
+
*
|
|
31
|
+
* `*` names any Object. A client sends back the ETag it was given, quotes and
|
|
32
|
+
* all, so both forms are accepted.
|
|
33
|
+
*/
|
|
34
|
+
export declare function simS3ETagMatches(sent: string, etag: string): boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SimS3NoSuchKey, SimS3NotImplemented, SimS3PreconditionFailed, } from "../../error/sim-s3.error.js";
|
|
2
|
+
import { simS3UnquotedETag } from "../../object/s3-object-etag.js";
|
|
3
|
+
/**
|
|
4
|
+
* The message real S3 gives with `PreconditionFailed`.
|
|
5
|
+
*/
|
|
6
|
+
export const simS3PreconditionMessage = "At least one of the pre-conditions you specified did not hold";
|
|
7
|
+
/**
|
|
8
|
+
* Refuse a write whose condition does not hold against the key's current
|
|
9
|
+
* Object.
|
|
10
|
+
*
|
|
11
|
+
* `If-None-Match: *` fails when the key holds an Object. `If-Match` fails when
|
|
12
|
+
* the Object's ETag differs, and answers `NoSuchKey` when the key holds
|
|
13
|
+
* nothing. A delete marker counts as nothing, because a read of the key finds
|
|
14
|
+
* nothing behind it. Real S3 accepts only `*` for `If-None-Match` on a write.
|
|
15
|
+
*/
|
|
16
|
+
export async function simS3RequireWritePrecondition(bucket, key, condition) {
|
|
17
|
+
const { IfMatch, IfNoneMatch } = condition;
|
|
18
|
+
if (IfMatch === undefined && IfNoneMatch === undefined) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (IfNoneMatch !== undefined && IfNoneMatch !== "*") {
|
|
22
|
+
throw new SimS3NotImplemented(`A header you provided implies functionality that is not implemented. ` +
|
|
23
|
+
`If-None-Match on a write takes only *.`);
|
|
24
|
+
}
|
|
25
|
+
const current = await bucket.getObject(key);
|
|
26
|
+
if (IfNoneMatch !== undefined && current !== undefined) {
|
|
27
|
+
throw new SimS3PreconditionFailed(simS3PreconditionMessage);
|
|
28
|
+
}
|
|
29
|
+
if (IfMatch === undefined) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (current === undefined) {
|
|
33
|
+
throw new SimS3NoSuchKey(`No S3 Object named ${key}`);
|
|
34
|
+
}
|
|
35
|
+
if (!simS3ETagMatches(IfMatch, current.etag)) {
|
|
36
|
+
throw new SimS3PreconditionFailed(simS3PreconditionMessage);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether an entity tag a client sent names an Object's ETag.
|
|
41
|
+
*
|
|
42
|
+
* `*` names any Object. A client sends back the ETag it was given, quotes and
|
|
43
|
+
* all, so both forms are accepted.
|
|
44
|
+
*/
|
|
45
|
+
export function simS3ETagMatches(sent, etag) {
|
|
46
|
+
return sent.trim() === "*" || simS3UnquotedETag(sent).trim() === etag;
|
|
47
|
+
}
|
|
@@ -3,6 +3,7 @@ import type { SimS3WriteBody } from "../../object/s3-write-body.js";
|
|
|
3
3
|
import type { SimS3ObjectWriteMetadata } from "../../object/s3-write-metadata.js";
|
|
4
4
|
import type { SimS3ObjectWriteStorage } from "../../object/s3-write-storage.js";
|
|
5
5
|
import type { SimS3ObjectWriteTagging } from "../../object/s3-write-tagging.js";
|
|
6
|
+
import type { SimS3WritePrecondition } from "../object/sim-s3-write-precondition.js";
|
|
6
7
|
/**
|
|
7
8
|
* Minimal structural sim S3 PutObject command.
|
|
8
9
|
*/
|
|
@@ -16,7 +17,7 @@ export interface SimPutObjectCommand {
|
|
|
16
17
|
* Object carries, which is why they are declared once in
|
|
17
18
|
* `SimS3ObjectWriteMetadata` and shared with `CreateMultipartUpload`.
|
|
18
19
|
*/
|
|
19
|
-
export interface SimPutObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging {
|
|
20
|
+
export interface SimPutObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging, SimS3WritePrecondition {
|
|
20
21
|
readonly Bucket?: string | undefined;
|
|
21
22
|
readonly Key?: string | undefined;
|
|
22
23
|
readonly Body?: SimPutObjectBody;
|
|
@@ -5,6 +5,7 @@ import { requireSimS3Bucket } from "../require-sim-s3-bucket.js";
|
|
|
5
5
|
import { PutObjectAuthorizer } from "./put-object-authorizer.js";
|
|
6
6
|
import { PutObjectBuilder } from "./put-object-builder.js";
|
|
7
7
|
import { simS3QuotedETag } from "../../object/s3-object-etag.js";
|
|
8
|
+
import { simS3RequireWritePrecondition } from "../object/sim-s3-write-precondition.js";
|
|
8
9
|
/**
|
|
9
10
|
* Simulated S3 PutObjectCommand handler.
|
|
10
11
|
*
|
|
@@ -45,7 +46,10 @@ export class PutObjectCommandHandler {
|
|
|
45
46
|
await this.background.sequence();
|
|
46
47
|
const caller = this.authorizer.authorize(bucket, Key, options);
|
|
47
48
|
const object = this.objectBuilder.build(command, bucket.getEncryption().algorithm);
|
|
48
|
-
const version = await bucket.
|
|
49
|
+
const version = await bucket.writeKey(Key, async () => {
|
|
50
|
+
await simS3RequireWritePrecondition(bucket, Key, command.input);
|
|
51
|
+
return await bucket.putObject(object);
|
|
52
|
+
});
|
|
49
53
|
const versionId = version?.versionId;
|
|
50
54
|
// Every write path funnels through here, so this one call covers the SDK,
|
|
51
55
|
// an intercepted SDK client and the REST endpoint alike.
|
|
@@ -199,3 +199,13 @@ export declare class SimS3InvalidTag extends SimS3Error {
|
|
|
199
199
|
readonly name = "InvalidTag";
|
|
200
200
|
constructor(message: string);
|
|
201
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Simulated S3 PreconditionFailed error.
|
|
204
|
+
*
|
|
205
|
+
* Real S3 answers this when a conditional request's condition does not hold.
|
|
206
|
+
* A write refused this way leaves the Object under its key as it was.
|
|
207
|
+
*/
|
|
208
|
+
export declare class SimS3PreconditionFailed extends SimS3Error {
|
|
209
|
+
readonly name = "PreconditionFailed";
|
|
210
|
+
constructor(message: string);
|
|
211
|
+
}
|
|
@@ -234,3 +234,15 @@ export class SimS3InvalidTag extends SimS3Error {
|
|
|
234
234
|
super(message, { httpStatusCode: 400 });
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
+
/**
|
|
238
|
+
* Simulated S3 PreconditionFailed error.
|
|
239
|
+
*
|
|
240
|
+
* Real S3 answers this when a conditional request's condition does not hold.
|
|
241
|
+
* A write refused this way leaves the Object under its key as it was.
|
|
242
|
+
*/
|
|
243
|
+
export class SimS3PreconditionFailed extends SimS3Error {
|
|
244
|
+
name = "PreconditionFailed";
|
|
245
|
+
constructor(message) {
|
|
246
|
+
super(message, { httpStatusCode: 412 });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -87,10 +87,11 @@ function listingEntries(request) {
|
|
|
87
87
|
entries.push({ name: object.key, object });
|
|
88
88
|
continue;
|
|
89
89
|
}
|
|
90
|
-
if (commonPrefix
|
|
91
|
-
|
|
92
|
-
entries.push({ name: commonPrefix });
|
|
90
|
+
if (commonPrefix === rolledUp) {
|
|
91
|
+
continue;
|
|
93
92
|
}
|
|
93
|
+
rolledUp = commonPrefix;
|
|
94
|
+
entries.push({ name: commonPrefix });
|
|
94
95
|
}
|
|
95
96
|
return entries;
|
|
96
97
|
}
|
|
@@ -31,6 +31,10 @@ export declare function listObjectsInput(request: SimS3ApiRequest): object;
|
|
|
31
31
|
* an absent member and an empty one differently.
|
|
32
32
|
*/
|
|
33
33
|
export declare function optional(name: string, value: string | null): Record<string, string>;
|
|
34
|
+
/**
|
|
35
|
+
* The date form of the same, dropping a value that is not a date.
|
|
36
|
+
*/
|
|
37
|
+
export declare function optionalDate(name: string, value: string | null): Record<string, Date>;
|
|
34
38
|
/**
|
|
35
39
|
* The numeric form of the same, dropping a value that is not a number.
|
|
36
40
|
*/
|
|
@@ -61,6 +61,16 @@ export function listObjectsInput(request) {
|
|
|
61
61
|
export function optional(name, value) {
|
|
62
62
|
return value === null ? {} : { [name]: value };
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* The date form of the same, dropping a value that is not a date.
|
|
66
|
+
*/
|
|
67
|
+
export function optionalDate(name, value) {
|
|
68
|
+
if (value === null) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
const parsed = new Date(value);
|
|
72
|
+
return Number.isNaN(parsed.getTime()) ? {} : { [name]: parsed };
|
|
73
|
+
}
|
|
64
74
|
/**
|
|
65
75
|
* The numeric form of the same, dropping a value that is not a number.
|
|
66
76
|
*/
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { optional, optionalNumber } from "./sim-s3-api-input.js";
|
|
2
|
+
import { writePreconditionInput } from "./sim-s3-api-object-input.js";
|
|
2
3
|
import { readSimS3CompletedUpload } from "./sim-s3-api-xml-read.js";
|
|
3
4
|
/**
|
|
4
5
|
* Reading the multipart upload operations' inputs out of a request.
|
|
@@ -33,6 +34,7 @@ export function uploadPartInput(request) {
|
|
|
33
34
|
export function completeMultipartUploadInput(request) {
|
|
34
35
|
return {
|
|
35
36
|
...uploadInput(request),
|
|
37
|
+
...writePreconditionInput(request),
|
|
36
38
|
MultipartUpload: readSimS3CompletedUpload(request.body),
|
|
37
39
|
};
|
|
38
40
|
}
|
|
@@ -35,6 +35,12 @@ export declare function getObjectInput(request: SimS3ApiRequest): object;
|
|
|
35
35
|
* them.
|
|
36
36
|
*/
|
|
37
37
|
export declare function putObjectInput(request: SimS3ApiRequest): object;
|
|
38
|
+
/**
|
|
39
|
+
* The conditions a write places on the Object already under its key, which
|
|
40
|
+
* PutObject, CopyObject and CompleteMultipartUpload all read from the same two
|
|
41
|
+
* headers.
|
|
42
|
+
*/
|
|
43
|
+
export declare function writePreconditionInput(request: SimS3ApiRequest): object;
|
|
38
44
|
/**
|
|
39
45
|
* The input of a copy, which names its source in a header and carries no bytes
|
|
40
46
|
* of its own.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { simS3WriteMetadataHeaders } from "../../object/s3-write-metadata.js";
|
|
2
|
-
import { optional, utf8 } from "./sim-s3-api-input.js";
|
|
2
|
+
import { optional, optionalDate, utf8 } from "./sim-s3-api-input.js";
|
|
3
3
|
import { readSimS3Tagging } from "./sim-s3-api-tagging-read.js";
|
|
4
4
|
/**
|
|
5
5
|
* Reading the Object operations' inputs out of a request.
|
|
@@ -54,7 +54,22 @@ export function getObjectInput(request) {
|
|
|
54
54
|
* them.
|
|
55
55
|
*/
|
|
56
56
|
export function putObjectInput(request) {
|
|
57
|
-
return {
|
|
57
|
+
return {
|
|
58
|
+
...createMultipartUploadInput(request),
|
|
59
|
+
...writePreconditionInput(request),
|
|
60
|
+
Body: request.body,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The conditions a write places on the Object already under its key, which
|
|
65
|
+
* PutObject, CopyObject and CompleteMultipartUpload all read from the same two
|
|
66
|
+
* headers.
|
|
67
|
+
*/
|
|
68
|
+
export function writePreconditionInput(request) {
|
|
69
|
+
return {
|
|
70
|
+
...optional("IfMatch", request.headers.get("if-match")),
|
|
71
|
+
...optional("IfNoneMatch", request.headers.get("if-none-match")),
|
|
72
|
+
};
|
|
58
73
|
}
|
|
59
74
|
/**
|
|
60
75
|
* The input of a copy, which names its source in a header and carries no bytes
|
|
@@ -67,11 +82,28 @@ export function putObjectInput(request) {
|
|
|
67
82
|
export function copyObjectInput(request) {
|
|
68
83
|
return {
|
|
69
84
|
...createMultipartUploadInput(request),
|
|
85
|
+
...writePreconditionInput(request),
|
|
86
|
+
...copySourcePreconditionInput(request),
|
|
70
87
|
...optional("CopySource", request.headers.get(simS3CopySourceHeader)),
|
|
71
88
|
...optional("MetadataDirective", request.headers.get("x-amz-metadata-directive")),
|
|
72
89
|
...optional("TaggingDirective", request.headers.get("x-amz-tagging-directive")),
|
|
73
90
|
};
|
|
74
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* The conditions a copy places on its source Object.
|
|
94
|
+
*
|
|
95
|
+
* The two date headers are HTTP dates, which the SDK hands the operation as
|
|
96
|
+
* `Date` values.
|
|
97
|
+
*/
|
|
98
|
+
function copySourcePreconditionInput(request) {
|
|
99
|
+
const header = (name) => request.headers.get(`x-amz-copy-source-if-${name}`);
|
|
100
|
+
return {
|
|
101
|
+
...optional("CopySourceIfMatch", header("match")),
|
|
102
|
+
...optional("CopySourceIfNoneMatch", header("none-match")),
|
|
103
|
+
...optionalDate("CopySourceIfModifiedSince", header("modified-since")),
|
|
104
|
+
...optionalDate("CopySourceIfUnmodifiedSince", header("unmodified-since")),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
75
107
|
/**
|
|
76
108
|
* The input of a request that describes an Object without carrying its bytes,
|
|
77
109
|
* which is what starting a multipart upload is.
|
|
@@ -60,14 +60,15 @@ export class FilesystemS3ObjectKeys {
|
|
|
60
60
|
nestedKeyPromises.push(this.listInDirectory(entryPath));
|
|
61
61
|
continue;
|
|
62
62
|
}
|
|
63
|
-
if (entry.isFile()) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
if (!entry.isFile()) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const key = path
|
|
67
|
+
.relative(this.directoryPath, entryPath)
|
|
68
|
+
.split(path.sep)
|
|
69
|
+
.join("/");
|
|
70
|
+
if (this.safety.isAllowedObjectKeyExtension(key)) {
|
|
71
|
+
keys.push(key);
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
const nestedKeys = await Promise.all(nestedKeyPromises);
|
|
@@ -18,29 +18,21 @@ export function secretTemplate(properties = {}) {
|
|
|
18
18
|
}
|
|
19
19
|
function secretProperties(properties) {
|
|
20
20
|
const { name, description, kmsKeyId, tags, secretString } = properties;
|
|
21
|
-
const declared = {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
SecretStringTemplate: "{}",
|
|
37
|
-
GenerateStringKey: "current",
|
|
38
|
-
PasswordLength: properties.passwordLength ?? 64,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
declared["SecretString"] = secretString;
|
|
43
|
-
}
|
|
21
|
+
const declared = {
|
|
22
|
+
...(name !== undefined && { Name: name }),
|
|
23
|
+
...(description !== undefined && { Description: description }),
|
|
24
|
+
...(kmsKeyId !== undefined && { KmsKeyId: kmsKeyId }),
|
|
25
|
+
...(tags !== undefined && { Tags: [...tags] }),
|
|
26
|
+
...(secretString === undefined
|
|
27
|
+
? {
|
|
28
|
+
GenerateSecretString: {
|
|
29
|
+
SecretStringTemplate: "{}",
|
|
30
|
+
GenerateStringKey: "current",
|
|
31
|
+
PasswordLength: properties.passwordLength ?? 64,
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
: { SecretString: secretString }),
|
|
35
|
+
};
|
|
44
36
|
return declared;
|
|
45
37
|
}
|
|
46
38
|
function readerResource() {
|
|
@@ -126,10 +126,10 @@ export class SimSqsMessage {
|
|
|
126
126
|
"ApproximateFirstReceiveTimestamp",
|
|
127
127
|
String(this.visibility.firstReceived.getTime()),
|
|
128
128
|
],
|
|
129
|
+
...(this.deadLetterSourceArn === undefined
|
|
130
|
+
? []
|
|
131
|
+
: [["DeadLetterQueueSourceArn", this.deadLetterSourceArn]]),
|
|
129
132
|
]);
|
|
130
|
-
if (this.deadLetterSourceArn !== undefined) {
|
|
131
|
-
values.set("DeadLetterQueueSourceArn", this.deadLetterSourceArn);
|
|
132
|
-
}
|
|
133
133
|
return values;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
@@ -56,9 +56,10 @@ export class SimStateMachine {
|
|
|
56
56
|
*/
|
|
57
57
|
update(changes) {
|
|
58
58
|
this.#roleArn = changes.roleArn ?? this.#roleArn;
|
|
59
|
-
if (changes.definition
|
|
60
|
-
|
|
61
|
-
this.#parsed = changes.parsed;
|
|
59
|
+
if (changes.definition === undefined || changes.parsed === undefined) {
|
|
60
|
+
return;
|
|
62
61
|
}
|
|
62
|
+
this.#definition = changes.definition;
|
|
63
|
+
this.#parsed = changes.parsed;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records the names code reads from the host process.env.
|
|
3
|
+
*
|
|
4
|
+
* SimLambdaEnvironmentConflicts warns that a module-scope read sees the host
|
|
5
|
+
* value of a variable instead of the function's own. Whether that matters
|
|
6
|
+
* depends on a read having happened at all, which configuration cannot tell.
|
|
7
|
+
* This keeps the evidence: the host environment is handed out behind a Proxy
|
|
8
|
+
* that notes each name read through it.
|
|
9
|
+
*
|
|
10
|
+
* Enumerating the whole environment is not counted as reading every name in
|
|
11
|
+
* it. Node.js copies process.env key by key when it spawns a child process,
|
|
12
|
+
* and so does any tool that snapshots it, and none of that is handler code
|
|
13
|
+
* capturing a value it later depends on. An enumeration is recognised by its
|
|
14
|
+
* shape: the property reads that follow a listing of the keys, in the order
|
|
15
|
+
* the listing gave them. Its reads are recorded as they happen and taken off
|
|
16
|
+
* the record again once every key has been read, so a listing that is not
|
|
17
|
+
* followed by a complete copy hides nothing. A module copying the environment
|
|
18
|
+
* with a spread at module scope is missed as a result.
|
|
19
|
+
*/
|
|
20
|
+
export declare class SimProcessEnvironmentReads {
|
|
21
|
+
private readonly names;
|
|
22
|
+
/**
|
|
23
|
+
* The environment objects each watching Proxy stands in front of, so that
|
|
24
|
+
* assigning one back to process.env does not stack a Proxy on a Proxy.
|
|
25
|
+
*/
|
|
26
|
+
private readonly targets;
|
|
27
|
+
/**
|
|
28
|
+
* The keys of an enumeration in progress, and how far its reads have got.
|
|
29
|
+
*/
|
|
30
|
+
private enumeration;
|
|
31
|
+
/**
|
|
32
|
+
* Whether code has read the name through a watched environment.
|
|
33
|
+
*/
|
|
34
|
+
wasRead(name: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* A view of the environment that records the names read through it.
|
|
37
|
+
*
|
|
38
|
+
* Writes and deletes go straight to the environment, so a value assigned
|
|
39
|
+
* through the view is coerced to a string as process.env would coerce it.
|
|
40
|
+
*/
|
|
41
|
+
watch(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
42
|
+
/**
|
|
43
|
+
* The environment behind a watching view, or the environment itself when it
|
|
44
|
+
* is not one.
|
|
45
|
+
*/
|
|
46
|
+
unwrap(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
47
|
+
/**
|
|
48
|
+
* Note a read, and take an enumeration's reads off the record once it has
|
|
49
|
+
* read every key.
|
|
50
|
+
*/
|
|
51
|
+
private recordRead;
|
|
52
|
+
}
|