-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apply Settings to export #51
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,7 +45,7 @@ cd import && npm test | |
``` | ||
|
||
# Running Local Wallet Import/Export | ||
Start the server. This command will run a simple static server on port 8080. | ||
Start the server. This command will run a simple static server on port 3000. | ||
```sh | ||
npm start | ||
``` | ||
|
@@ -55,7 +55,7 @@ Clone the `sdk` repo. | |
git clone [email protected]:tkhq/sdk.git | ||
``` | ||
|
||
Follow the README.md for the `wallet-export` [example](https://github.com/tkhq/sdk/tree/main/examples/wallet-export). Set the `NEXT_PUBLIC_EXPORT_IFRAME_URL="http://localhost:3000/"` in the example's environment variables configuration. The `wallet-export` example embeds this page as an iframe. | ||
Follow the README.md for the `wallet-export` [example](https://github.com/tkhq/sdk/tree/main/examples/wallet-export). Set the `NEXT_PUBLIC_EXPORT_IFRAME_URL="http://localhost:3000/index.template"` in the example's environment variables configuration. The `wallet-export` example embeds this page as an iframe. | ||
```sh | ||
cd sdk/examples/wallet-export | ||
``` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -160,6 +160,7 @@ <h2>Message log</h2> | |
> | ||
</p> | ||
<div id="message-log"></div> | ||
<div id="key-div"></div> | ||
|
||
<!-- Script containing ED25519 library --> | ||
<script src="noble-hashes.js"></script> | ||
|
@@ -172,6 +173,7 @@ <h2>Message log</h2> | |
window.TKHQ = (function () { | ||
/** constant for LocalStorage */ | ||
const TURNKEY_EMBEDDED_KEY = "TURNKEY_EMBEDDED_KEY"; | ||
const TURNKEY_SETTINGS = "TURNKEY_SETTINGS"; | ||
/** 48 hours in milliseconds */ | ||
const TURNKEY_EMBEDDED_KEY_TTL_IN_MILLIS = 1000 * 60 * 60 * 48; | ||
|
||
|
@@ -246,6 +248,25 @@ <h2>Message log</h2> | |
window.localStorage.removeItem(TURNKEY_EMBEDDED_KEY); | ||
} | ||
|
||
/** | ||
* Gets the current settings. | ||
*/ | ||
function getSettings() { | ||
const settings = window.localStorage.getItem(TURNKEY_SETTINGS); | ||
return settings ? JSON.parse(settings) : null; | ||
} | ||
|
||
/** | ||
* Sets the settings object. | ||
* @param {Object} settings | ||
*/ | ||
function setSettings(settings) { | ||
window.localStorage.setItem( | ||
TURNKEY_SETTINGS, | ||
JSON.stringify(settings) | ||
); | ||
} | ||
|
||
/** | ||
* Set an item in localStorage with an expiration time | ||
* @param {string} key | ||
|
@@ -671,6 +692,111 @@ <h2>Message log</h2> | |
return nobleEd25519.getPublicKey(privateKeyHex); | ||
} | ||
|
||
/** | ||
* Function to validate and sanitize the styles object using the accepted map of style keys and values (as regular expressions). | ||
* Any invalid style throws an error. Returns an object of valid styles. | ||
* @param {Object} styles | ||
* @return {Object} | ||
*/ | ||
function validateStyles(styles, element) { | ||
const validStyles = {}; | ||
|
||
const cssValidationRegex = { | ||
padding: "^(\\d+(px|em|%|rem) ?){1,4}$", | ||
margin: "^(\\d+(px|em|%|rem) ?){1,4}$", | ||
borderWidth: "^(\\d+(px|em|rem) ?){1,4}$", | ||
borderStyle: | ||
"^(none|solid|dashed|dotted|double|groove|ridge|inset|outset)$", | ||
borderColor: | ||
"^(transparent|inherit|initial|#[0-9a-f]{3,8}|rgba?\\(\\d{1,3}, \\d{1,3}, \\d{1,3}(, \\d?(\\.\\d{1,2})?)?\\)|hsla?\\(\\d{1,3}, \\d{1,3}%, \\d{1,3}%(, \\d?(\\.\\d{1,2})?)?\\))$", | ||
borderRadius: "^(\\d+(px|em|%|rem) ?){1,4}$", | ||
fontSize: | ||
"^(\\d+(px|em|rem|%|vh|vw|in|cm|mm|pt|pc|ex|ch|vmin|vmax))$", | ||
fontWeight: "^(normal|bold|bolder|lighter|\\d{3})$", | ||
fontFamily: '^[^";<>]*$', // checks for the absence of some characters that could lead to CSS/HTML injection | ||
color: | ||
"^(transparent|inherit|initial|#[0-9a-f]{3,8}|rgba?\\(\\d{1,3}, \\d{1,3}, \\d{1,3}(, \\d?(\\.\\d{1,2})?)?\\)|hsla?\\(\\d{1,3}, \\d{1,3}%, \\d{1,3}%(, \\d?(\\.\\d{1,2})?)?\\))$", | ||
backgroundColor: | ||
"^(transparent|inherit|initial|#[0-9a-f]{3,8}|rgba?\\(\\d{1,3}, \\d{1,3}, \\d{1,3}(, \\d?(\\.\\d{1,2})?)?\\)|hsla?\\(\\d{1,3}, \\d{1,3}%, \\d{1,3}%(, \\d?(\\.\\d{1,2})?)?\\))$", | ||
width: | ||
"^(\\d+(px|em|rem|%|vh|vw|in|cm|mm|pt|pc|ex|ch|vmin|vmax)|auto)$", | ||
height: | ||
"^(\\d+(px|em|rem|%|vh|vw|in|cm|mm|pt|pc|ex|ch|vmin|vmax)|auto)$", | ||
maxWidth: | ||
"^(\\d+(px|em|rem|%|vh|vw|in|cm|mm|pt|pc|ex|ch|vmin|vmax)|none)$", | ||
maxHeight: | ||
"^(\\d+(px|em|rem|%|vh|vw|in|cm|mm|pt|pc|ex|ch|vmin|vmax)|none)$", | ||
lineHeight: | ||
"^(\\d+(\\.\\d+)?(px|em|rem|%|vh|vw|in|cm|mm|pt|pc|ex|ch|vmin|vmax)|normal)$", | ||
boxShadow: | ||
"^(none|(\\d+(px|em|rem) ?){2,4} (#[0-9a-f]{3,8}|rgba?\\(\\d{1,3}, \\d{1,3}, \\d{1,3}(, \\d?(\\.\\d{1,2})?)?\\)) ?(inset)?)$", | ||
textAlign: "^(left|right|center|justify|initial|inherit)$", | ||
overflowWrap: "^(normal|break-word|anywhere)$", | ||
wordWrap: "^(normal|break-word)$", | ||
resize: "^(none|both|horizontal|vertical|block|inline)$", | ||
}; | ||
|
||
Object.entries(styles).forEach(([property, value]) => { | ||
const styleProperty = property.trim(); | ||
if (styleProperty.length === 0) { | ||
throw new Error("css style property cannot be empty"); | ||
} | ||
const styleRegexStr = cssValidationRegex[styleProperty]; | ||
if (!styleRegexStr) { | ||
throw new Error( | ||
`invalid or unsupported css style property: "${styleProperty}"` | ||
); | ||
} | ||
const styleRegex = new RegExp(styleRegexStr); | ||
const styleValue = value.trim(); | ||
if (styleValue.length == 0) { | ||
throw new Error(`css style for "${styleProperty}" is empty`); | ||
} | ||
const isValidStyle = styleRegex.test(styleValue); | ||
if (!isValidStyle) { | ||
throw new Error( | ||
`invalid css style value for property "${styleProperty}"` | ||
); | ||
} | ||
validStyles[styleProperty] = styleValue; | ||
}); | ||
|
||
return validStyles; | ||
} | ||
|
||
/** | ||
* Function to apply settings on this page. For now, the only settings that can be applied | ||
* are for "styles". Upon successful application, return the valid, sanitized settings JSON string. | ||
* @param {string} settings | ||
* @return {string} | ||
*/ | ||
function applySettings(settings) { | ||
const validSettings = {}; | ||
if (!settings) { | ||
return JSON.stringify(validSettings); | ||
} | ||
const settingsObj = JSON.parse(settings); | ||
if (settingsObj.styles) { | ||
// Valid styles will be applied the "key-div" div HTML element. | ||
const keyDivTextarea = document.getElementById("key-div"); | ||
if (!keyDivTextarea) { | ||
throw new Error( | ||
"no key-div HTML element found to apply settings to." | ||
); | ||
} | ||
|
||
// Validate, sanitize, and apply the styles to the "key-div" div element. | ||
const validStyles = TKHQ.validateStyles(settingsObj.styles); | ||
Object.entries(validStyles).forEach(([key, value]) => { | ||
keyDivTextarea.style[key] = value; | ||
}); | ||
|
||
validSettings["styles"] = validStyles; | ||
} | ||
|
||
return JSON.stringify(validSettings); | ||
} | ||
|
||
return { | ||
initEmbeddedKey, | ||
generateTargetKey, | ||
|
@@ -693,6 +819,10 @@ <h2>Message log</h2> | |
additionalAssociatedData, | ||
verifyEnclaveSignature, | ||
getEd25519PublicKey, | ||
applySettings, | ||
validateStyles, | ||
getSettings, | ||
setSettings, | ||
}; | ||
})(); | ||
</script> | ||
|
@@ -709,6 +839,11 @@ <h2>Message log</h2> | |
document.addEventListener( | ||
"DOMContentLoaded", | ||
async () => { | ||
// If styles are saved in local storage, sanitize and apply them. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may have already done this, and nothing indicates the contrary, but would be nice to do a sanity check to make sure these loaded settings can be overridden if the user calls There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes this works as exepected! |
||
const styleSettings = TKHQ.getSettings(); | ||
if (styleSettings) { | ||
TKHQ.applySettings(styleSettings); | ||
} | ||
await TKHQ.initEmbeddedKey(); | ||
const embeddedKeyJwk = await TKHQ.getEmbeddedKey(); | ||
const targetPubBuf = await TKHQ.p256JWKPrivateToPublic( | ||
|
@@ -764,6 +899,13 @@ <h2>Message log</h2> | |
TKHQ.sendMessageUp("ERROR", e.toString()); | ||
} | ||
} | ||
if (event.data && event.data["type"] == "APPLY_SETTINGS") { | ||
try { | ||
await onApplySettings(event.data["value"]); | ||
} catch (e) { | ||
TKHQ.sendMessageUp("ERROR", e.toString()); | ||
} | ||
} | ||
}, | ||
false | ||
); | ||
|
@@ -820,7 +962,7 @@ <h2>Message log</h2> | |
*/ | ||
function displayKey(key) { | ||
Array.from(document.body.children).forEach((child) => { | ||
if (child.tagName !== "SCRIPT") { | ||
if (child.tagName !== "SCRIPT" && child.id !== "key-div") { | ||
child.style.display = "none"; | ||
} | ||
}); | ||
|
@@ -835,13 +977,13 @@ <h2>Message log</h2> | |
}; | ||
|
||
// Create a new div with the key material and append the new div to the body | ||
const keyDiv = document.createElement("div"); | ||
keyDiv.id = "key-div"; | ||
const keyDiv = document.getElementById("key-div"); | ||
keyDiv.innerText = key; | ||
for (let styleKey in style) { | ||
keyDiv.style[styleKey] = style[styleKey]; | ||
} | ||
document.body.appendChild(keyDiv); | ||
TKHQ.applySettings(TKHQ.getSettings()); | ||
} | ||
|
||
/** | ||
|
@@ -1018,6 +1160,23 @@ <h2>Message log</h2> | |
TKHQ.sendMessageUp("BUNDLE_INJECTED", true); | ||
} | ||
|
||
/** | ||
* Function triggered when APPLY_SETTINGS event is received. | ||
* For now, the only settings that can be applied are for "styles". | ||
* Persist them in local storage so they can be applied on every | ||
* page load. | ||
*/ | ||
async function onApplySettings(settings) { | ||
// Apply settings | ||
const validSettings = TKHQ.applySettings(settings); | ||
|
||
// Persist in local storage | ||
TKHQ.setSettings(validSettings); | ||
|
||
// Send up SETTINGS_APPLIED message | ||
TKHQ.sendMessageUp("SETTINGS_APPLIED", true); | ||
} | ||
|
||
/** | ||
* Decrypt the ciphertext (ArrayBuffer) given an encapsulation key (ArrayBuffer) | ||
* and the receivers private key (JSON Web Key). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might make sense to put these valid, modifiable css properties in a doc somewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea, can add something to the UI customization page