mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-19 07:01:35 +00:00
fix: decode Route53 octal escapes in record names (#5553)
* fix: decode Route53 octal escapes in record names * fix: keep escaped dots and backslashes in Route53 record names
This commit is contained in:
@@ -111,6 +111,54 @@ describe("route53Client.listRecords", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("decodes octal escapes such as \\052 for wildcard names", async () => {
|
||||
send.mockResolvedValueOnce({
|
||||
ResourceRecordSets: [
|
||||
{
|
||||
Name: "\\052.example.com.",
|
||||
Type: "A",
|
||||
TTL: 300,
|
||||
ResourceRecords: [{ Value: "1.2.3.4" }],
|
||||
},
|
||||
],
|
||||
IsTruncated: false,
|
||||
});
|
||||
|
||||
const records = await route53Client.listRecords(config, "Z123");
|
||||
|
||||
expect(records[0]).toMatchObject({
|
||||
id: "A:*.example.com",
|
||||
name: "*.example.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps escaped dots so they stay distinct from label separators", async () => {
|
||||
send.mockResolvedValueOnce({
|
||||
ResourceRecordSets: [
|
||||
{
|
||||
Name: "a\\056b.example.com.",
|
||||
Type: "A",
|
||||
TTL: 300,
|
||||
ResourceRecords: [{ Value: "1.2.3.4" }],
|
||||
},
|
||||
{
|
||||
Name: "a.b.example.com.",
|
||||
Type: "A",
|
||||
TTL: 300,
|
||||
ResourceRecords: [{ Value: "5.6.7.8" }],
|
||||
},
|
||||
],
|
||||
IsTruncated: false,
|
||||
});
|
||||
|
||||
const records = await route53Client.listRecords(config, "Z123");
|
||||
|
||||
expect(records.map((r) => r.id)).toEqual([
|
||||
"A:a\\056b.example.com",
|
||||
"A:a.b.example.com",
|
||||
]);
|
||||
});
|
||||
|
||||
it("skips record sets with no ResourceRecords (e.g. alias targets)", async () => {
|
||||
send.mockResolvedValueOnce({
|
||||
ResourceRecordSets: [
|
||||
@@ -355,6 +403,24 @@ describe("route53Client.deleteRecord", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("finds a wildcard record set that Route53 returns escaped", async () => {
|
||||
const existingSet = {
|
||||
Name: "\\052.example.com.",
|
||||
Type: "A",
|
||||
TTL: 300,
|
||||
ResourceRecords: [{ Value: "1.2.3.4" }],
|
||||
};
|
||||
send.mockResolvedValueOnce({ ResourceRecordSets: [existingSet] });
|
||||
send.mockResolvedValueOnce({});
|
||||
|
||||
await route53Client.deleteRecord(config, "Z123", "A:*.example.com");
|
||||
|
||||
const changeCommand = send.mock.calls[1]?.[0] as HasInput;
|
||||
expect(changeCommand.input.ChangeBatch.Changes).toEqual([
|
||||
{ Action: "DELETE", ResourceRecordSet: existingSet },
|
||||
]);
|
||||
});
|
||||
|
||||
it("throws when the record no longer exists", async () => {
|
||||
send.mockResolvedValueOnce({ ResourceRecordSets: [] });
|
||||
|
||||
|
||||
@@ -27,6 +27,14 @@ const stripTrailingDot = (name: string) => name.replace(/\.$/, "");
|
||||
const ensureTrailingDot = (name: string) =>
|
||||
name.endsWith(".") ? name : `${name}.`;
|
||||
const stripZonePrefix = (id: string) => id.replace(/^\/hostedzone\//, "");
|
||||
// Route53 returns special characters in names as \ + three-digit octal code (e.g. * as \052).
|
||||
// Escaped dots (\056) and backslashes (\134) stay escaped so decoded names remain unique.
|
||||
const decodeRecordName = (name: string) =>
|
||||
name.replace(/\\([0-7]{3})/g, (match, code) =>
|
||||
code === "056" || code === "134"
|
||||
? match
|
||||
: String.fromCharCode(Number.parseInt(code, 8)),
|
||||
);
|
||||
|
||||
const buildRecordId = (type: string, name: string) =>
|
||||
`${type}:${stripTrailingDot(name)}`;
|
||||
@@ -61,7 +69,8 @@ const findExactRecordSet = async (
|
||||
if (
|
||||
candidate &&
|
||||
candidate.Type === type &&
|
||||
stripTrailingDot(candidate.Name ?? "") === stripTrailingDot(name)
|
||||
stripTrailingDot(decodeRecordName(candidate.Name ?? "")) ===
|
||||
stripTrailingDot(name)
|
||||
) {
|
||||
return candidate;
|
||||
}
|
||||
@@ -132,10 +141,11 @@ export const route53Client: DnsClient<Route53Config> = {
|
||||
if (!set.Name || !set.Type || !set.ResourceRecords?.length) {
|
||||
continue;
|
||||
}
|
||||
const name = decodeRecordName(set.Name);
|
||||
records.push({
|
||||
id: buildRecordId(set.Type, set.Name),
|
||||
id: buildRecordId(set.Type, name),
|
||||
type: set.Type,
|
||||
name: stripTrailingDot(set.Name),
|
||||
name: stripTrailingDot(name),
|
||||
content: set.ResourceRecords.map((r) => r.Value).join("\n"),
|
||||
ttl: set.TTL ?? 300,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user