[autofix.ci] apply automated fixes

This commit is contained in:
autofix-ci[bot]
2025-04-26 07:40:07 +00:00
committed by GitHub
parent e6d0b7b4ee
commit c3986d7a08
4 changed files with 116 additions and 69 deletions

View File

@@ -58,10 +58,10 @@ describe("processTemplate", () => {
variables: { variables: {
jwt_secret: "cQsdycq1hDLopQonF6jUTqgQc5WEZTwWLL02J6XJ", jwt_secret: "cQsdycq1hDLopQonF6jUTqgQc5WEZTwWLL02J6XJ",
anon_payload: JSON.stringify({ anon_payload: JSON.stringify({
"role": "tester", role: "tester",
"iss": "dockploy", iss: "dockploy",
"iat": "${timestamps:2025-01-01T00:00:00Z}", iat: "${timestamps:2025-01-01T00:00:00Z}",
"exp": "${timestamps:2030-01-01T00:00:00Z}", exp: "${timestamps:2030-01-01T00:00:00Z}",
}), }),
anon_key: "${jwt:jwt_secret:anon_payload}", anon_key: "${jwt:jwt_secret:anon_payload}",
}, },
@@ -74,7 +74,9 @@ describe("processTemplate", () => {
}; };
const result = processTemplate(template, mockSchema); const result = processTemplate(template, mockSchema);
expect(result.envs).toHaveLength(1); expect(result.envs).toHaveLength(1);
expect(result.envs).toContain("ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNzM1Njg5NjAwIiwiZXhwIjoiMTg5MzQ1NjAwMCIsInJvbGUiOiJ0ZXN0ZXIiLCJpc3MiOiJkb2NrcGxveSJ9.BG5JoxL2_NaTFbPgyZdm3kRWenf_O3su_HIRKGCJ_kY"); expect(result.envs).toContain(
"ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNzM1Njg5NjAwIiwiZXhwIjoiMTg5MzQ1NjAwMCIsInJvbGUiOiJ0ZXN0ZXIiLCJpc3MiOiJkb2NrcGxveSJ9.BG5JoxL2_NaTFbPgyZdm3kRWenf_O3su_HIRKGCJ_kY",
);
expect(result.mounts).toHaveLength(0); expect(result.mounts).toHaveLength(0);
expect(result.domains).toHaveLength(0); expect(result.domains).toHaveLength(0);
}); });

View File

@@ -2,7 +2,6 @@ import type { Schema } from "@dokploy/server/templates";
import { processValue } from "@dokploy/server/templates/processors"; import { processValue } from "@dokploy/server/templates/processors";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
describe("helpers functions", () => { describe("helpers functions", () => {
// Mock schema for testing // Mock schema for testing
const mockSchema: Schema = { const mockSchema: Schema = {
@@ -30,7 +29,11 @@ describe("helpers functions", () => {
it("should generate a random domain", () => { it("should generate a random domain", () => {
const domain = processValue("${domain}", {}, mockSchema); const domain = processValue("${domain}", {}, mockSchema);
expect(domain.startsWith(`${mockSchema.projectName}-`)).toBeTruthy(); expect(domain.startsWith(`${mockSchema.projectName}-`)).toBeTruthy();
expect(domain.endsWith(`${mockSchema.serverIp.replaceAll(".","-")}.traefik.me`)).toBeTruthy(); expect(
domain.endsWith(
`${mockSchema.serverIp.replaceAll(".", "-")}.traefik.me`,
),
).toBeTruthy();
}); });
}); });
@@ -46,11 +49,14 @@ describe("helpers functions", () => {
[32, 44], [32, 44],
[64, 88], [64, 88],
[128, 172], [128, 172],
])("should generate a base64 string from parameter %d bytes length", (length, finalLength) => { ])(
const base64 = processValue(`\${base64:${length}}`, {}, mockSchema); "should generate a base64 string from parameter %d bytes length",
expect(base64).toMatch(/^[A-Za-z0-9+=/]+={0,2}$/); (length, finalLength) => {
expect(base64.length).toBe(finalLength); const base64 = processValue(`\${base64:${length}}`, {}, mockSchema);
}); expect(base64).toMatch(/^[A-Za-z0-9+=/]+={0,2}$/);
expect(base64.length).toBe(finalLength);
},
);
}); });
describe("${password}", () => { describe("${password}", () => {
@@ -58,11 +64,14 @@ describe("helpers functions", () => {
const password = processValue("${password}", {}, mockSchema); const password = processValue("${password}", {}, mockSchema);
expect(password).toMatch(/^[A-Za-z0-9]+$/); expect(password).toMatch(/^[A-Za-z0-9]+$/);
}); });
it.each([6,8,12,16,32])("should generate a password string respecting parameter %d length", (length) => { it.each([6, 8, 12, 16, 32])(
const password = processValue(`\${password:${length}}`, {}, mockSchema); "should generate a password string respecting parameter %d length",
expect(password).toMatch(/^[A-Za-z0-9]+$/); (length) => {
expect(password.length).toBe(length); const password = processValue(`\${password:${length}}`, {}, mockSchema);
}); expect(password).toMatch(/^[A-Za-z0-9]+$/);
expect(password.length).toBe(length);
},
);
}); });
describe("${hash}", () => { describe("${hash}", () => {
@@ -70,17 +79,22 @@ describe("helpers functions", () => {
const hash = processValue("${hash}", {}, mockSchema); const hash = processValue("${hash}", {}, mockSchema);
expect(hash).toMatch(/^[A-Za-z0-9]+$/); expect(hash).toMatch(/^[A-Za-z0-9]+$/);
}); });
it.each([6,8,12,16,32])("should generate a hash string respecting parameter %d length", (length) => { it.each([6, 8, 12, 16, 32])(
const hash = processValue(`\${hash:${length}}`, {}, mockSchema); "should generate a hash string respecting parameter %d length",
expect(hash).toMatch(/^[A-Za-z0-9]+$/); (length) => {
expect(hash.length).toBe(length); const hash = processValue(`\${hash:${length}}`, {}, mockSchema);
}); expect(hash).toMatch(/^[A-Za-z0-9]+$/);
expect(hash.length).toBe(length);
},
);
}); });
describe("${uuid}", () => { describe("${uuid}", () => {
it("should generate a UUID string", () => { it("should generate a UUID string", () => {
const uuid = processValue("${uuid}", {}, mockSchema); const uuid = processValue("${uuid}", {}, mockSchema);
expect(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); expect(uuid).toMatch(
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
);
}); });
}); });
@@ -100,8 +114,12 @@ describe("helpers functions", () => {
expect(timestamp.length).toBe(nowLength); expect(timestamp.length).toBe(nowLength);
}); });
it("should generate a timestamp string in milliseconds from parameter", () => { it("should generate a timestamp string in milliseconds from parameter", () => {
const timestamp = processValue("${timestampms:2025-01-01}", {}, mockSchema); const timestamp = processValue(
expect(timestamp).toEqual('1735689600000'); "${timestampms:2025-01-01}",
{},
mockSchema,
);
expect(timestamp).toEqual("1735689600000");
}); });
}); });
describe("${timestamps}", () => { describe("${timestamps}", () => {
@@ -112,8 +130,12 @@ describe("helpers functions", () => {
expect(timestamps.length).toBe(nowLength); expect(timestamps.length).toBe(nowLength);
}); });
it("should generate a timestamp string in seconds from parameter", () => { it("should generate a timestamp string in seconds from parameter", () => {
const timestamps = processValue("${timestamps:2025-01-01}", {}, mockSchema); const timestamps = processValue(
expect(timestamps).toEqual('1735689600'); "${timestamps:2025-01-01}",
{},
mockSchema,
);
expect(timestamps).toEqual("1735689600");
}); });
}); });
@@ -146,39 +168,50 @@ describe("helpers functions", () => {
const parts = jwt.split(".") as JWTParts; const parts = jwt.split(".") as JWTParts;
const decodedPayload = jwtBase64Decode(parts[1]); const decodedPayload = jwtBase64Decode(parts[1]);
jwtCheckHeader(parts[0]); jwtCheckHeader(parts[0]);
expect (decodedPayload).toHaveProperty("iat"); expect(decodedPayload).toHaveProperty("iat");
expect (decodedPayload).toHaveProperty("iss"); expect(decodedPayload).toHaveProperty("iss");
expect (decodedPayload).toHaveProperty("exp"); expect(decodedPayload).toHaveProperty("exp");
expect (decodedPayload.iss).toEqual("dokploy"); expect(decodedPayload.iss).toEqual("dokploy");
});
it.each([6,8,12,16,32])("should generate a random hex string from parameter %d byte length", (length) => {
const jwt = processValue(`\${jwt:${length}}`, {}, mockSchema);
expect(jwt).toMatch(/^[A-Za-z0-9-_.]+$/);
expect(jwt.length).toBeGreaterThanOrEqual(length); // bytes translated to hex can take up to 2x the length
expect(jwt.length).toBeLessThanOrEqual(length * 2);
}); });
it.each([6, 8, 12, 16, 32])(
"should generate a random hex string from parameter %d byte length",
(length) => {
const jwt = processValue(`\${jwt:${length}}`, {}, mockSchema);
expect(jwt).toMatch(/^[A-Za-z0-9-_.]+$/);
expect(jwt.length).toBeGreaterThanOrEqual(length); // bytes translated to hex can take up to 2x the length
expect(jwt.length).toBeLessThanOrEqual(length * 2);
},
);
}); });
describe("${jwt:secret}", () => { describe("${jwt:secret}", () => {
it("should generate a JWT string respecting parameter secret from variable", () => { it("should generate a JWT string respecting parameter secret from variable", () => {
const jwt = processValue("${jwt:secret}", {secret: "mysecret"}, mockSchema); const jwt = processValue(
"${jwt:secret}",
{ secret: "mysecret" },
mockSchema,
);
expect(jwt).toMatch(jwtMatchExp); expect(jwt).toMatch(jwtMatchExp);
const parts = jwt.split(".") as JWTParts; const parts = jwt.split(".") as JWTParts;
const decodedPayload = jwtBase64Decode(parts[1]); const decodedPayload = jwtBase64Decode(parts[1]);
jwtCheckHeader(parts[0]); jwtCheckHeader(parts[0]);
expect (decodedPayload).toHaveProperty("iat"); expect(decodedPayload).toHaveProperty("iat");
expect (decodedPayload).toHaveProperty("iss"); expect(decodedPayload).toHaveProperty("iss");
expect (decodedPayload).toHaveProperty("exp"); expect(decodedPayload).toHaveProperty("exp");
expect (decodedPayload.iss).toEqual("dokploy"); expect(decodedPayload.iss).toEqual("dokploy");
}); });
}); });
describe("${jwt:secret:payload}", () => { describe("${jwt:secret:payload}", () => {
it("should generate a JWT string respecting parameters secret and payload from variables", () => { it("should generate a JWT string respecting parameters secret and payload from variables", () => {
const iat = Math.floor(new Date("2025-01-01T00:00:00Z").getTime() / 1000); const iat = Math.floor(new Date("2025-01-01T00:00:00Z").getTime() / 1000);
const expiry = iat + 3600; const expiry = iat + 3600;
const jwt = processValue("${jwt:secret:payload}", { const jwt = processValue(
secret: "mysecret", "${jwt:secret:payload}",
payload: `{"iss": "test-issuer", "iat": ${iat}, "exp": ${expiry}, "customprop": "customvalue"}`, {
}, mockSchema); secret: "mysecret",
payload: `{"iss": "test-issuer", "iat": ${iat}, "exp": ${expiry}, "customprop": "customvalue"}`,
},
mockSchema,
);
expect(jwt).toMatch(jwtMatchExp); expect(jwt).toMatch(jwtMatchExp);
const parts = jwt.split(".") as JWTParts; const parts = jwt.split(".") as JWTParts;
jwtCheckHeader(parts[0]); jwtCheckHeader(parts[0]);
@@ -191,7 +224,9 @@ describe("helpers functions", () => {
expect(decodedPayload.exp).toEqual(expiry); expect(decodedPayload.exp).toEqual(expiry);
expect(decodedPayload).toHaveProperty("customprop"); expect(decodedPayload).toHaveProperty("customprop");
expect(decodedPayload.customprop).toEqual("customvalue"); expect(decodedPayload.customprop).toEqual("customvalue");
expect(jwt).toEqual("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzU2ODk2MDAsImV4cCI6MTczNTY5MzIwMCwiaXNzIjoidGVzdC1pc3N1ZXIiLCJjdXN0b21wcm9wIjoiY3VzdG9tdmFsdWUifQ.m42U7PZSUSCf7gBOJrxJir0rQmyPq4rA59Dydr_QahI") expect(jwt).toEqual(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzU2ODk2MDAsImV4cCI6MTczNTY5MzIwMCwiaXNzIjoidGVzdC1pc3N1ZXIiLCJjdXN0b21wcm9wIjoiY3VzdG9tdmFsdWUifQ.m42U7PZSUSCf7gBOJrxJir0rQmyPq4rA59Dydr_QahI",
);
}); });
}); });
}); });

View File

@@ -9,7 +9,7 @@ import { fetchTemplateFiles } from "./github";
export interface Schema { export interface Schema {
serverIp: string; serverIp: string;
projectName: string; projectName: string;
}; }
export type DomainSchema = Pick<Domain, "host" | "port" | "serviceName"> & { export type DomainSchema = Pick<Domain, "host" | "port" | "serviceName"> & {
path?: string; path?: string;
@@ -22,7 +22,7 @@ export interface Template {
content: string; content: string;
}>; }>;
domains: DomainSchema[]; domains: DomainSchema[];
}; }
export interface GenerateJWTOptions { export interface GenerateJWTOptions {
length?: number; length?: number;
@@ -65,17 +65,16 @@ export const generatePassword = (quantity = 16): string => {
*/ */
export function generateBase64(bytes = 32): string { export function generateBase64(bytes = 32): string {
return randomBytes(bytes).toString("base64"); return randomBytes(bytes).toString("base64");
}; }
function safeBase64(str: string): string { function safeBase64(str: string): string {
return str return str.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
.replace(/=/g, "") }
.replace(/\+/g, "-")
.replace(/\//g, "_");
};
function objToJWTBase64(obj: any): string { function objToJWTBase64(obj: any): string {
return safeBase64(Buffer.from(JSON.stringify(obj), "utf8").toString("base64")); return safeBase64(
}; Buffer.from(JSON.stringify(obj), "utf8").toString("base64"),
);
}
export function generateJwt(options: GenerateJWTOptions = {}): string { export function generateJwt(options: GenerateJWTOptions = {}): string {
let { length, secret, payload = {} } = options; let { length, secret, payload = {} } = options;
@@ -88,16 +87,21 @@ export function generateJwt(options: GenerateJWTOptions = {}): string {
}); });
payload.iss || (payload.iss = "dokploy"); payload.iss || (payload.iss = "dokploy");
payload.iat || (payload.iat = Math.floor(Date.now() / 1000)); payload.iat || (payload.iat = Math.floor(Date.now() / 1000));
payload.exp || (payload.exp = Math.floor(new Date("2030-01-01T00:00:00Z").getTime() / 1000)); payload.exp ||
(payload.exp = Math.floor(
new Date("2030-01-01T00:00:00Z").getTime() / 1000,
));
const encodedPayload = objToJWTBase64({ const encodedPayload = objToJWTBase64({
iat: Math.floor(Date.now() / 1000), iat: Math.floor(Date.now() / 1000),
exp: Math.floor(new Date("2030-01-01T00:00:00Z").getTime() / 1000), exp: Math.floor(new Date("2030-01-01T00:00:00Z").getTime() / 1000),
...payload, ...payload,
}); });
secret || (secret = randomBytes(32).toString("hex")); secret || (secret = randomBytes(32).toString("hex"));
const signature = safeBase64(createHmac("SHA256", secret) const signature = safeBase64(
.update(`${encodedHeader}.${encodedPayload}`) createHmac("SHA256", secret)
.digest("base64")); .update(`${encodedHeader}.${encodedPayload}`)
.digest("base64"),
);
return `${encodedHeader}.${encodedPayload}.${signature}`; return `${encodedHeader}.${encodedPayload}.${signature}`;
} }

View File

@@ -117,7 +117,9 @@ export function processValue(
return new Date(varName.slice(12)).getTime().toString(); return new Date(varName.slice(12)).getTime().toString();
} }
if (varName.startsWith("timestamps:")) { if (varName.startsWith("timestamps:")) {
return Math.round(new Date(varName.slice(11)).getTime() / 1000).toString(); return Math.round(
new Date(varName.slice(11)).getTime() / 1000,
).toString();
} }
if (varName === "randomPort") { if (varName === "randomPort") {
@@ -129,15 +131,19 @@ export function processValue(
} }
if (varName.startsWith("jwt:")) { if (varName.startsWith("jwt:")) {
const params:string[] = varName.split(":").slice(1); const params: string[] = varName.split(":").slice(1);
if (params.length === 1 && params[0] && params[0].match(/^\d{1,3}$/)) { if (params.length === 1 && params[0] && params[0].match(/^\d{1,3}$/)) {
return generateJwt({length: Number.parseInt(params[0], 10)}); return generateJwt({ length: Number.parseInt(params[0], 10) });
} }
let [secret, payload] = params; let [secret, payload] = params;
if (typeof payload === "string" && variables[payload]) { if (typeof payload === "string" && variables[payload]) {
payload = variables[payload]; payload = variables[payload];
} }
if (typeof payload === "string" && payload.startsWith("{") && payload.endsWith("}")) { if (
typeof payload === "string" &&
payload.startsWith("{") &&
payload.endsWith("}")
) {
try { try {
payload = JSON.parse(payload); payload = JSON.parse(payload);
} catch (e) { } catch (e) {
@@ -146,12 +152,12 @@ export function processValue(
console.error("Invalid JWT payload", e); console.error("Invalid JWT payload", e);
} }
} }
if (typeof payload !== 'object') { if (typeof payload !== "object") {
payload = undefined; payload = undefined;
} }
return generateJwt({ return generateJwt({
secret: secret ? (variables[secret] || secret) : undefined, secret: secret ? variables[secret] || secret : undefined,
payload: payload as any payload: payload as any,
}); });
} }