-
Notifications
You must be signed in to change notification settings - Fork 1
[MOC-57] Load modifications on edit mode #66
Changes from all commits
746cef5
f8e4b78
0d91c8b
04827f2
c9190b2
1af4685
7e6685a
b697c25
f30c89b
f3c9318
87c6e31
527b57d
6932d3e
48eb2e0
54ca6b0
ab716e0
828e2ec
c8036b6
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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import { DOMManipulator } from "@repo/dodom"; | ||
import sanitizeHtml from "sanitize-html"; | ||
import MocksiRollbar from "./MocksiRollbar"; | ||
import type { Alteration } from "./background"; | ||
|
@@ -14,6 +15,8 @@ import { | |
STORAGE_KEY, | ||
SignupURL, | ||
} from "./consts"; | ||
import { fragmentTextNode } from "./content/EditMode/actions"; | ||
import { getHighlighter } from "./content/EditMode/highlighter"; | ||
|
||
type DOMModificationsType = { | ||
[querySelector: string]: { nextText: string; previousText: string }; | ||
|
@@ -40,7 +43,7 @@ export const logout = () => { | |
|
||
const commandsExecuted: Command[] = []; | ||
|
||
const domainModifications: DOMModificationsType = {}; | ||
let domainModifications: DOMModificationsType = {}; | ||
|
||
export const saveModification = ( | ||
parentElement: HTMLElement, | ||
|
@@ -74,17 +77,35 @@ export const persistModifications = (recordingId: string) => { | |
export const undoModifications = () => { | ||
loadPreviousModifications(); | ||
chrome.storage.local.remove(MOCKSI_MODIFICATIONS); | ||
getHighlighter().removeHighlightNodes(); | ||
// clean the domainModifications | ||
domainModifications = {}; | ||
}; | ||
|
||
// v2 of loading alterations, this is from backend | ||
export const loadAlterations = (alterations: Alteration[] | null) => { | ||
export const loadAlterations = ( | ||
alterations: Alteration[] | null, | ||
withHighlights: boolean, | ||
) => { | ||
undoModifications(); | ||
if (!alterations?.length) { | ||
// FIXME: we should warn the user that there are no alterations for this demo | ||
return [] as Alteration[]; | ||
} | ||
const domManipulator = new DOMManipulator( | ||
fragmentTextNode, | ||
getHighlighter(), | ||
saveModification, | ||
); | ||
for (const alteration of alterations) { | ||
const { selector, dom_after, dom_before } = alteration; | ||
modifyElementInnerHTML(selector, dom_before, sanitizeHtml(dom_after)); | ||
const elemToModify = getHTMLElementFromSelector(selector); | ||
domManipulator.iterateAndReplace( | ||
elemToModify as Node, | ||
new RegExp(dom_before, "gi"), | ||
sanitizeHtml(dom_after), | ||
withHighlights, | ||
); | ||
} | ||
}; | ||
|
||
|
@@ -93,75 +114,52 @@ export const loadPreviousModifications = () => { | |
for (const modification of Object.entries(domainModifications)) { | ||
const [querySelector, { previousText, nextText }] = modification; | ||
const sanitizedPreviousText = sanitizeHtml(previousText); | ||
const elemToModify = getHTMLElementFromSelector(querySelector); | ||
// here newText and previous is in altered order because we want to revert the changes | ||
modifyElementInnerHTML(querySelector, nextText, sanitizedPreviousText); | ||
} | ||
}; | ||
|
||
const modifyElementInnerHTML = ( | ||
selector: string, | ||
oldContent: string, | ||
newContent: string, | ||
) => { | ||
// querySelector format {htmlElementType}#{elementId}.{elementClassnames}[${elementIndexIfPresent}]{{newValue}} | ||
const hasIndex = selector.match(/\[[0-9]+\]/); | ||
const valueInQuerySelector = selector.match(/\{[a-zA-Z0-9 ]+\}/); // add spaces to pattern | ||
let elemToModify: Element | null; | ||
// FIXME: this needs to be refactored | ||
if (hasIndex) { | ||
// with all this replaces, we should build a formatter | ||
const filteredQuerySelector = formatQuerySelector( | ||
selector, | ||
valueInQuerySelector, | ||
hasIndex, | ||
); | ||
const index: number = +hasIndex[0].replace("[", "").replace("]", ""); | ||
// FIXME: lots of duplicated code here | ||
try { | ||
elemToModify = document.querySelectorAll(filteredQuerySelector)[index]; | ||
} catch (e: unknown) { | ||
if (e instanceof Error) { | ||
console.error(`Error querying selector: ${e}`); | ||
} | ||
|
||
elemToModify = null; | ||
} | ||
} else { | ||
// FIXME: lots of duplicated code here | ||
try { | ||
elemToModify = document.querySelector( | ||
formatQuerySelector(selector, valueInQuerySelector, null), | ||
if (elemToModify) { | ||
elemToModify.innerHTML = elemToModify.innerHTML.replaceAll( | ||
nextText, | ||
sanitizedPreviousText, | ||
Comment on lines
+117
to
+122
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. Tip Codebase Verification The
Analysis chainThe Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify that `innerHTML` replacements do not introduce XSS vulnerabilities.
# Test: Check for proper sanitization of `nextText` before insertion.
echo "Check sanitization of nextText in loadPreviousModifications"
Length of output: 128 Script: #!/bin/bash
# Locate the `loadPreviousModifications` function in the utils.ts file.
ast-grep --lang typescript --pattern 'function loadPreviousModifications($_) { $$$ }' apps/mocksi-lite/utils.ts
Length of output: 113 Script: #!/bin/bash
# Search for the `loadPreviousModifications` function definition in the utils.ts file using ripgrep.
rg 'function loadPreviousModifications' apps/mocksi-lite/utils.ts
Length of output: 67 Script: #!/bin/bash
# Search for any references to `loadPreviousModifications` in the utils.ts file.
rg 'loadPreviousModifications' apps/mocksi-lite/utils.ts
Length of output: 135 Script: #!/bin/bash
# Extract the implementation of the `loadPreviousModifications` function from the utils.ts file.
ast-grep --lang typescript --pattern 'const loadPreviousModifications = () => { $$$ }' apps/mocksi-lite/utils.ts
Length of output: 1075 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. nextText is only to search and replace with the previousText. 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. @NicoMoreinoSirius, thank you for clarifying the usage of However, as a best practice and to safeguard against potential future changes that might alter how these variables are used, it might be worth considering sanitizing Let me know if you would like me to assist further or if there's anything else you'd like to address! |
||
); | ||
} catch (e: unknown) { | ||
if (e instanceof Error) { | ||
console.error(`Error querying selector: ${e}`); | ||
} | ||
elemToModify = null; | ||
} | ||
} | ||
const sanitizedNewContent = sanitizeHtml(newContent); | ||
if (elemToModify?.innerHTML) { | ||
elemToModify.innerHTML = | ||
elemToModify.innerHTML.replaceAll(oldContent, sanitizedNewContent) || ""; | ||
} | ||
}; | ||
|
||
const formatQuerySelector = ( | ||
rawSelector: string, | ||
valueInQuerySelector: RegExpMatchArray | null, | ||
hasIndex: RegExpMatchArray | null, | ||
) => { | ||
if (hasIndex) { | ||
return valueInQuerySelector | ||
? rawSelector | ||
.replace(hasIndex[0], "") | ||
.replace(valueInQuerySelector[0], "") | ||
: rawSelector.replace(hasIndex[0], ""); | ||
} | ||
// querySelector format {htmlElementType}#{elementId}.{elementClassnames}[${elementIndexIfPresent}]{{newValue}} | ||
const [index] = hasIndex || [""]; | ||
const [value] = valueInQuerySelector || [""]; | ||
return rawSelector.replace(index, "").replace(value, ""); | ||
}; | ||
|
||
return valueInQuerySelector | ||
? rawSelector.replace(valueInQuerySelector[0], "") | ||
: rawSelector; | ||
const getHTMLElementFromSelector = ( | ||
unfomattedSelector: string, | ||
): Element | null => { | ||
const hasIndex = unfomattedSelector.match(/\[[0-9]+\]/); | ||
const valueInQuerySelector = unfomattedSelector.match(/\{.+\}/); | ||
const formattedSelector = formatQuerySelector( | ||
unfomattedSelector, | ||
hasIndex, | ||
valueInQuerySelector, | ||
); | ||
let elemToModify: NodeListOf<Element> | null; | ||
try { | ||
elemToModify = document.querySelectorAll(formattedSelector); | ||
} catch (e: unknown) { | ||
if (e instanceof Error) { | ||
console.error(`Error querying selector: ${e}`); | ||
} | ||
elemToModify = null; | ||
} | ||
if (elemToModify) { | ||
const index = hasIndex ? +hasIndex[0].replace("[", "").replace("]", "") : 0; | ||
return elemToModify[index]; | ||
} | ||
return elemToModify; | ||
}; | ||
|
||
export const sendMessage = ( | ||
|
@@ -235,7 +233,7 @@ export const getEmail = async (): Promise<string | null> => { | |
export const getRecordingsStorage = async (): Promise<Recording[]> => { | ||
try { | ||
const results = await chrome.storage.local.get(["recordings"]); | ||
console.log(results.recordings); | ||
console.log("results", results.recordings); | ||
if (results.recordings) { | ||
return JSON.parse(results.recordings); | ||
} | ||
|
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.
Tip
Codebase Verification
Ensure that all references to
ContentHighlighter
are replaced withgetHighlighter()
to maintain consistency with the new instantiation pattern.apps/mocksi-lite/content/ContentApp.tsx
:const ContentHighlighter = getHighlighter();
ContentHighlighter.showHideHighlights(!prevValue);
Analysis chain
Ensure that all references to
ContentHighlighter
are replaced withgetHighlighter()
to maintain consistency with the new instantiation pattern.Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 111
Script:
Length of output: 248