-
-
Save tschoffelen/5fa4ca1841ee09f96c9a0f21b4787eff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class Plist { | |
options: Record<string, any>; | |
constructor(options: Record<string, any>) { | |
this.options = options; | |
} | |
toXML(): string { | |
const plistHeader = `<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
<plist version="1.0">`; | |
const plistFooter = `</plist>`; | |
const dictEntries = Object.entries(this.options) | |
.map(([key, value]) => { | |
if (typeof value === "boolean") { | |
return `<key>${key}</key><${value ? "true" : "false"}/>`; | |
} else if (typeof value === "string") { | |
return `<key>${key}</key><string>${value}</string>`; | |
} else if (Array.isArray(value)) { | |
return `<key>${key}</key><array>${value.map((item) => `<string>${item}</string>`).join("")}</array>`; | |
} else if (typeof value === "number") { | |
return `<key>${key}</key><integer>${value}</integer>`; | |
} | |
return ""; | |
}) | |
.join(""); | |
return `${plistHeader}<dict>${dictEntries}</dict>${plistFooter}`; | |
} | |
/** | |
* Converts the plist options to a SEB JSON format. | |
* https://safeexambrowser.org/developer/seb-config-key.html | |
* | |
* @return {string} The SEB JSON string representation of the options. | |
*/ | |
toSebJSON(): string { | |
const json = { ...this.options }; | |
if (json.originatorVersion) delete json.originatorVersion; // Remove originatorVersion if it exists | |
// Order the the JSON by keys alphabetically | |
const orderedJson = Object.keys(json) | |
.sort() | |
.reduce((acc, key) => { | |
acc[key] = json[key]; | |
return acc; | |
}, {}); | |
return JSON.stringify(orderedJson); | |
} | |
async _hash(str: string): Promise<string> { | |
const encoder = new TextEncoder(); | |
const data = encoder.encode(str); | |
const hash = await crypto.subtle.digest("SHA-256", data); | |
const hashArray = Array.from(new Uint8Array(hash)); | |
return hashArray | |
.map((byte) => byte.toString(16).padStart(2, "0")) | |
.join("") | |
.toLowerCase(); | |
} | |
/** | |
* Generate a SHA256 hash from the SEB-JSON string | |
* Encode the hash value as a Base16 string. The result should be a 32 Bytes, 64 chars string. | |
* | |
* @returns {Promise<string>} The SHA256 hash of the SEB JSON string, for a specific start URL. | |
*/ | |
async getConfigKey(): Promise<string> { | |
const sebJson = this.toSebJSON(); | |
const configKey = await this._hash(sebJson); | |
return await this._hash(this.options.startURL + configKey); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment