Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2024-05-18 - Cross-Site Scripting (XSS) via injectJavaScript in WebView
**Vulnerability:** The application was vulnerable to Cross-Site Scripting (XSS) due to the use of `injectJavaScript` to pass data (base64 JSON strings) directly into the WebView execution context in `src/screens/HomeScreen.tsx` and `src/screens/ShiftScreen.tsx`.
**Learning:** Using `injectJavaScript` with string interpolation can allow malicious scripts to be executed within the WebView context, especially when handling arbitrary data like images or large strings.
**Prevention:** Avoid using `injectJavaScript` for passing data. Instead, establish a secure communication channel using `postMessage`. The WebView should listen for messages via `window.document.addEventListener('message', ...)` and `window.addEventListener('message', ...)`, and send a 'READY' message to the React Native app when it's initialized to prevent race conditions.
34 changes: 25 additions & 9 deletions src/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ window.runTesseract = async function(base64JsonStr) {
window.ReactNativeWebView.postMessage(JSON.stringify({ success: false, error: e.message || e.toString() }));
}
};

const handleMsg = function(e) {
try {
const data = JSON.parse(e.data);
if(data.type === "OCR_REQUEST") {
if(window.runTesseract) {
window.runTesseract(data.payload);
} else {
window.ReactNativeWebView.postMessage(JSON.stringify({ success: false, error: "Motore OCR non pronto." }));
}
}
} catch(err) {}
};
window.document.addEventListener('message', handleMsg);
window.addEventListener('message', handleMsg);

window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'READY' }));
</script></body></html>`;

function PinnedFlightCardComponent({ item, colors }: { item: any; colors: any }) {
Expand Down Expand Up @@ -166,6 +183,7 @@ export default function HomeScreen({ isFocused }: { isFocused?: boolean }) {
const [newEndM, setNewEndM] = useState('00');
const [uploadMode, setUploadMode] = useState<'image' | 'manual' | null>(null);
const [pinnedFlight, setPinnedFlight] = useState<any>(null);
const [isEngineReady, setIsEngineReady] = useState(false);

const webViewRef = useRef<WebView>(null);

Expand Down Expand Up @@ -283,22 +301,20 @@ export default function HomeScreen({ isFocused }: { isFocused?: boolean }) {
setProcessing(true); setOcrText('');
const base64List = result.assets.map(a => `data:image/jpeg;base64,${a.base64}`);
const base64Json = JSON.stringify(base64List);
// Use postMessage pattern to avoid script-injection risks with injectJavaScript
webViewRef.current?.injectJavaScript(`
if(window.runTesseract){
window.runTesseract(${JSON.stringify(base64Json)});
} else {
window.ReactNativeWebView.postMessage(JSON.stringify({success:false,error:'OCR non pronto'}));
}
true;
`);
if (!isEngineReady) {
Alert.alert("Errore", "OCR non pronto. Attendi qualche istante.");
setProcessing(false);
return;
}
webViewRef.current?.postMessage(JSON.stringify({ type: 'OCR_REQUEST', payload: base64Json }));
}
} catch (e) { if (__DEV__) console.error('[imagePicker]', e); setProcessing(false); }
};

const handleWebViewMessage = (event: any) => {
try {
const r = JSON.parse(event.nativeEvent.data);
if (r.type === 'READY') { setIsEngineReady(true); return; }
if (r.success) setOcrText(r.text);
else Alert.alert('Errore riconoscimento testo', r.error || 'Prova con un\'immagine piΓΉ nitida o meglio illuminata.');
} catch (e) { if (__DEV__) console.error('[ocrMessage]', e); } finally { setProcessing(false); }
Expand Down
37 changes: 28 additions & 9 deletions src/screens/ShiftScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function ShiftScreen() {
const [ocrText, setOcrText] = useState<string>('');
const [processing, setProcessing] = useState(false);
const webViewRef = useRef<WebView>(null);
const [isEngineReady, setIsEngineReady] = useState(false);

const pickImage = async () => {
try {
Expand All @@ -34,15 +35,12 @@ export default function ShiftScreen() {
const base64List = result.assets.map(a => `data:image/jpeg;base64,${a.base64}`);
const base64Json = JSON.stringify(base64List).replace(/'/g, "\\'");

const jsCode = `
if (window.runTesseract) {
window.runTesseract('${base64Json}');
} else {
window.ReactNativeWebView.postMessage(JSON.stringify({ success: false, error: "Motore OCR non pronto." }));
}
true;
`;
webViewRef.current?.injectJavaScript(jsCode);
if (!isEngineReady) {
Alert.alert("Errore", "Motore OCR non pronto. Riprova.");
setProcessing(false);
return;
}
webViewRef.current?.postMessage(JSON.stringify({ type: 'OCR_REQUEST', payload: base64Json }));
}
} catch (e) {
Alert.alert("Errore OCR", "Impossibile elaborare l'immagine.");
Expand All @@ -54,6 +52,10 @@ export default function ShiftScreen() {
const rawData = event.nativeEvent.data;
try {
const result = JSON.parse(rawData);
if (result.type === 'READY') {
setIsEngineReady(true);
return;
}
if (result.success) {
setOcrText(result.text);
} else {
Expand Down Expand Up @@ -230,6 +232,23 @@ export default function ShiftScreen() {
window.ReactNativeWebView.postMessage(JSON.stringify({ success: false, error: e.message || e.toString() }));
}
};

const handleMsg = function(e) {
try {
const data = JSON.parse(e.data);
if(data.type === "OCR_REQUEST") {
if(window.runTesseract) {
window.runTesseract(data.payload);
} else {
window.ReactNativeWebView.postMessage(JSON.stringify({ success: false, error: "Motore OCR non pronto." }));
}
}
} catch(err) {}
};
window.document.addEventListener('message', handleMsg);
window.addEventListener('message', handleMsg);

window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'READY' }));
</script>
</body>
</html>
Expand Down
Loading