-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbackground.ts
More file actions
138 lines (116 loc) · 3.7 KB
/
background.ts
File metadata and controls
138 lines (116 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/**
* Background Service Worker for React2Shell Detector
*
* This service worker runs in the background and handles scanning requests
* from the popup. It has host_permissions which allow it to bypass CORS
* restrictions when sending the safe payload to target URLs.
*/
import { isVulnerableSafeCheck } from "~lib/detector"
import { buildRequestHeaders, buildSafePayload } from "~lib/payload"
import type { Message, ScanError, ScanRequest, ScanResult } from "~lib/types"
import { normalizeUrl } from "~lib/utils"
/**
* Default timeout for scan requests (10 seconds, matches Python script)
*/
const DEFAULT_TIMEOUT = 10000
/**
* Handle scan request from popup
*/
async function handleScanRequest(
request: ScanRequest,
sendResponse: (response: ScanResult | ScanError) => void
) {
const { url, timeout = DEFAULT_TIMEOUT } = request
try {
// Normalize and validate URL
const normalizedUrl = normalizeUrl(url)
// Build the safe payload and headers
const { body, contentType } = buildSafePayload()
const headers = buildRequestHeaders(contentType)
// Create abort controller for timeout
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
// Send the payload to the target URL
const response = await fetch(normalizedUrl, {
method: "POST",
headers,
body,
signal: controller.signal,
redirect: "manual", // Don't follow redirects
cache: "no-cache"
})
clearTimeout(timeoutId)
// Read response body
const responseText = await response.text()
// Check if vulnerable
const vulnerable = isVulnerableSafeCheck(response, responseText)
// Build response headers object
const responseHeaders: Record<string, string> = {}
response.headers.forEach((value, key) => {
responseHeaders[key] = value
})
// Build request details for display
const requestDetails = {
method: "POST",
url: normalizedUrl,
headers,
body: body.slice(0, 500) // Truncate for display
}
// Send successful result back to popup
const result: ScanResult = {
type: "SCAN_RESULT",
success: true,
vulnerable,
url: normalizedUrl,
statusCode: response.status,
statusText: response.statusText,
headers: responseHeaders,
responseBody: responseText.slice(0, 2000), // Truncate to 2000 chars
timestamp: new Date().toISOString(),
requestDetails
}
sendResponse(result)
} catch (error) {
// Handle errors (network, timeout, etc.)
let errorMessage = "Unknown error"
if (error instanceof Error) {
if (error.name === "AbortError") {
errorMessage = "Request timed out"
} else if (error.message.includes("Failed to fetch")) {
errorMessage = "Network error (CORS, DNS, or connection failed)"
} else {
errorMessage = error.message
}
}
const errorResult: ScanError = {
type: "SCAN_ERROR",
success: false,
error: errorMessage,
url,
timestamp: new Date().toISOString()
}
sendResponse(errorResult)
}
}
/**
* Listen for messages from popup
*/
chrome.runtime.onMessage.addListener(
(
message: Message,
sender: chrome.runtime.MessageSender,
sendResponse: (response: ScanResult | ScanError) => void
) => {
// Only handle SCAN_START messages
if (message.type === "SCAN_START") {
// Handle the scan request asynchronously
handleScanRequest(message, sendResponse)
// Return true to indicate we will send a response asynchronously
return true
}
return false
}
)
// Log when service worker starts
console.log("React2Shell Detector background service worker started")
export {}