@indigoai-us/hq-cloud 6.18.22 → 6.18.23
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/cli/sync.d.ts +6 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +733 -613
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +317 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/kms10-etag-safe-pull.d.ts +20 -0
- package/dist/kms10-etag-safe-pull.d.ts.map +1 -0
- package/dist/kms10-etag-safe-pull.js +37 -0
- package/dist/kms10-etag-safe-pull.js.map +1 -0
- package/dist/kms10-etag-safe-pull.test.d.ts +2 -0
- package/dist/kms10-etag-safe-pull.test.d.ts.map +1 -0
- package/dist/kms10-etag-safe-pull.test.js +53 -0
- package/dist/kms10-etag-safe-pull.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const KMS10_COPY_MARKER_META_KEY = "hq-kms10-reencrypt-current";
|
|
2
|
+
export declare const KMS10_COPY_ORIGINAL_KEY_HASH_META_KEY = "hq-kms10-original-key-sha256";
|
|
3
|
+
export declare function kms10OriginalKeyHash(key: string): string;
|
|
4
|
+
export interface Kms10RemoteRevision {
|
|
5
|
+
etag: string;
|
|
6
|
+
size: number;
|
|
7
|
+
}
|
|
8
|
+
export interface Kms10HeadRevision {
|
|
9
|
+
etag: string;
|
|
10
|
+
size: number;
|
|
11
|
+
metadata?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Prove that a changed ETag is only the re-encryption copy. The marker alone
|
|
15
|
+
* is insufficient: require the listed revision, byte count, and persisted
|
|
16
|
+
* content hash to match the prior journal baseline, or the source ETag to
|
|
17
|
+
* match the prior journal revision for older files without hash metadata.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isKms10CopyOfJournalBase(listed: Kms10RemoteRevision, head: Kms10HeadRevision | null, journalHash: string, journalEtag: string, expectedKey: string): boolean;
|
|
20
|
+
//# sourceMappingURL=kms10-etag-safe-pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kms10-etag-safe-pull.d.ts","sourceRoot":"","sources":["../src/kms10-etag-safe-pull.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,0BAA0B,+BAA+B,CAAC;AACvE,eAAO,MAAM,qCAAqC,iCAAiC,CAAC;AAEpF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,iBAAiB,GAAG,IAAI,EAC9B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAgBT"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { normalizeEtag } from "./journal.js";
|
|
3
|
+
import { contentHashMatchesRawSha256 } from "./sync/content-hash.js";
|
|
4
|
+
export const KMS10_COPY_MARKER_META_KEY = "hq-kms10-reencrypt-current";
|
|
5
|
+
export const KMS10_COPY_ORIGINAL_KEY_HASH_META_KEY = "hq-kms10-original-key-sha256";
|
|
6
|
+
export function kms10OriginalKeyHash(key) {
|
|
7
|
+
return createHash("sha256").update(key, "utf8").digest("hex");
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Prove that a changed ETag is only the re-encryption copy. The marker alone
|
|
11
|
+
* is insufficient: require the listed revision, byte count, and persisted
|
|
12
|
+
* content hash to match the prior journal baseline, or the source ETag to
|
|
13
|
+
* match the prior journal revision for older files without hash metadata.
|
|
14
|
+
*/
|
|
15
|
+
export function isKms10CopyOfJournalBase(listed, head, journalHash, journalEtag, expectedKey) {
|
|
16
|
+
if (!head || !journalHash)
|
|
17
|
+
return false;
|
|
18
|
+
if (head.metadata?.[KMS10_COPY_MARKER_META_KEY] !== "true")
|
|
19
|
+
return false;
|
|
20
|
+
// S3 copies source metadata by default. A later legitimate CopyObject to a
|
|
21
|
+
// different key can inherit the KMS-10 marker, so only accept the marker at
|
|
22
|
+
// the key the migration copied in place.
|
|
23
|
+
if (head.metadata?.[KMS10_COPY_ORIGINAL_KEY_HASH_META_KEY] !== kms10OriginalKeyHash(expectedKey))
|
|
24
|
+
return false;
|
|
25
|
+
if (normalizeEtag(head.etag) !== normalizeEtag(listed.etag))
|
|
26
|
+
return false;
|
|
27
|
+
if (head.size !== listed.size)
|
|
28
|
+
return false;
|
|
29
|
+
const copiedHash = head.metadata?.["hq-content-sha256"];
|
|
30
|
+
if (copiedHash !== undefined) {
|
|
31
|
+
// Older writers persist raw SHA-256 in S3 metadata; newer event envelopes
|
|
32
|
+
// use the tagged wire form accepted by contentHashMatchesRawSha256.
|
|
33
|
+
return copiedHash === journalHash || contentHashMatchesRawSha256(copiedHash, journalHash);
|
|
34
|
+
}
|
|
35
|
+
return normalizeEtag(head.metadata?.["hq-kms10-original-etag"] ?? "") === normalizeEtag(journalEtag);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=kms10-etag-safe-pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kms10-etag-safe-pull.js","sourceRoot":"","sources":["../src/kms10-etag-safe-pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,CAAC,MAAM,0BAA0B,GAAG,4BAA4B,CAAC;AACvE,MAAM,CAAC,MAAM,qCAAqC,GAAG,8BAA8B,CAAC;AAEpF,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAA2B,EAC3B,IAA8B,EAC9B,WAAmB,EACnB,WAAmB,EACnB,WAAmB;IAEnB,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,0BAA0B,CAAC,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,yCAAyC;IACzC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,qCAAqC,CAAC,KAAK,oBAAoB,CAAC,WAAW,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/G,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1E,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,mBAAmB,CAAC,CAAC;IACxD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,0EAA0E;QAC1E,oEAAoE;QACpE,OAAO,UAAU,KAAK,WAAW,IAAI,2BAA2B,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAC5F,CAAC;IACD,OAAO,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,wBAAwB,CAAC,IAAI,EAAE,CAAC,KAAK,aAAa,CAAC,WAAW,CAAC,CAAC;AACvG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kms10-etag-safe-pull.test.d.ts","sourceRoot":"","sources":["../src/kms10-etag-safe-pull.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { isKms10CopyOfJournalBase, kms10OriginalKeyHash } from "./kms10-etag-safe-pull.js";
|
|
3
|
+
const HASH = "a".repeat(64);
|
|
4
|
+
const REMOTE = { etag: '"after-copy"', size: 12 };
|
|
5
|
+
const HEAD = {
|
|
6
|
+
etag: '"after-copy"',
|
|
7
|
+
size: 12,
|
|
8
|
+
metadata: {
|
|
9
|
+
"hq-kms10-reencrypt-current": "true",
|
|
10
|
+
"hq-kms10-original-key-sha256": kms10OriginalKeyHash("docs/a.md"),
|
|
11
|
+
"hq-content-sha256": `sha256:${HASH}`,
|
|
12
|
+
"hq-kms10-original-etag": "before-copy",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
describe("KMS-10 ETag-only copy detection", () => {
|
|
16
|
+
it("recognizes a marked copy whose bytes match the journal baseline", () => {
|
|
17
|
+
expect(isKms10CopyOfJournalBase(REMOTE, HEAD, HASH, "before-copy", "docs/a.md")).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
it("accepts the raw SHA-256 value persisted in S3 user metadata", () => {
|
|
20
|
+
expect(isKms10CopyOfJournalBase(REMOTE, {
|
|
21
|
+
...HEAD,
|
|
22
|
+
metadata: { ...HEAD.metadata, "hq-content-sha256": HASH },
|
|
23
|
+
}, HASH, "before-copy", "docs/a.md")).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
it("does not trust a marker inherited by a copy to a different key", () => {
|
|
26
|
+
expect(isKms10CopyOfJournalBase(REMOTE, HEAD, HASH, "before-copy", "docs/other.md")).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it("does not suppress an ordinary write without the migration marker", () => {
|
|
29
|
+
expect(isKms10CopyOfJournalBase(REMOTE, {
|
|
30
|
+
...HEAD,
|
|
31
|
+
metadata: { "hq-content-sha256": `sha256:${HASH}` },
|
|
32
|
+
}, HASH, "before-copy", "docs/a.md")).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
it("does not suppress a copy whose bytes differ from the journal baseline", () => {
|
|
35
|
+
expect(isKms10CopyOfJournalBase(REMOTE, {
|
|
36
|
+
...HEAD,
|
|
37
|
+
metadata: { ...HEAD.metadata, "hq-content-sha256": `sha256:${"b".repeat(64)}` },
|
|
38
|
+
}, HASH, "before-copy", "docs/a.md")).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it("does not trust a HEAD result for a different listed revision", () => {
|
|
41
|
+
expect(isKms10CopyOfJournalBase(REMOTE, { ...HEAD, etag: '"newer-write"' }, HASH, "before-copy", "docs/a.md")).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it("does not suppress a size mismatch", () => {
|
|
44
|
+
expect(isKms10CopyOfJournalBase(REMOTE, { ...HEAD, size: 13 }, HASH, "before-copy", "docs/a.md")).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
it("uses the original ETag identity when legacy files have no content hash metadata", () => {
|
|
47
|
+
const { ["hq-content-sha256"]: _hash, ...legacyMetadata } = HEAD.metadata;
|
|
48
|
+
const legacyHead = { ...HEAD, metadata: legacyMetadata };
|
|
49
|
+
expect(isKms10CopyOfJournalBase(REMOTE, legacyHead, HASH, "before-copy", "docs/a.md")).toBe(true);
|
|
50
|
+
expect(isKms10CopyOfJournalBase(REMOTE, legacyHead, HASH, "different-baseline", "docs/a.md")).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=kms10-etag-safe-pull.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kms10-etag-safe-pull.test.js","sourceRoot":"","sources":["../src/kms10-etag-safe-pull.test.ts"],"names":[],"mappings":"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"}
|