merge Clipboard.qll and DragAndDrop.qll, and support InputEvent

This commit is contained in:
Erik Krogh Kristensen
2022-04-18 22:17:31 +02:00
parent 34abef8a6c
commit 7f592a6c64
7 changed files with 143 additions and 153 deletions

View File

@@ -79,7 +79,6 @@ import semmle.javascript.frameworks.ComposedFunctions
import semmle.javascript.frameworks.Classnames
import semmle.javascript.frameworks.ClassValidator
import semmle.javascript.frameworks.ClientRequests
import semmle.javascript.frameworks.Clipboard
import semmle.javascript.frameworks.ClosureLibrary
import semmle.javascript.frameworks.CookieLibraries
import semmle.javascript.frameworks.Credentials
@@ -88,7 +87,7 @@ import semmle.javascript.frameworks.D3
import semmle.javascript.frameworks.data.ModelsAsData
import semmle.javascript.frameworks.DateFunctions
import semmle.javascript.frameworks.DigitalOcean
import semmle.javascript.frameworks.DragAndDrop
import semmle.javascript.frameworks.DomEvents
import semmle.javascript.frameworks.Electron
import semmle.javascript.frameworks.EventEmitter
import semmle.javascript.frameworks.Files

View File

@@ -1,75 +0,0 @@
/**
* Provides predicates for reasoning about clipboard data.
*/
import javascript
/**
* Gets a jQuery "paste" event.
* E.g. `e` in `$("#foo").on("paste", function(e) { ... })`.
*/
private DataFlow::SourceNode jQueryPasteEvent(DataFlow::TypeTracker t) {
t.start() and
exists(DataFlow::CallNode call |
call = JQuery::objectRef().getAMethodCall(["bind", "on", "live", "one", "delegate"]) and
call.getArgument(0).mayHaveStringValue("paste")
|
result = call.getCallback(call.getNumArgument() - 1).getParameter(0)
)
or
exists(DataFlow::TypeTracker t2 | result = jQueryPasteEvent(t2).track(t2, t))
}
/**
* Gets a DOM "paste" event.
* E.g. `e` in `document.addEventListener("paste", e => { ... })`.
*/
private DataFlow::SourceNode pasteEvent(DataFlow::TypeTracker t) {
t.start() and
exists(DataFlow::CallNode call | call = DOM::domValueRef().getAMemberCall("addEventListener") |
call.getArgument(0).mayHaveStringValue("paste") and
result = call.getCallback(1).getParameter(0)
)
or
t.start() and
exists(DataFlow::ParameterNode pn |
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent
pn.hasUnderlyingType("ClipboardEvent") and result = pn
)
or
t.start() and
exists(DataFlow::PropWrite pw | pw = DOM::domValueRef().getAPropertyWrite() |
pw.getPropertyName() = "onpaste" and
result = pw.getRhs().getABoundFunctionValue(0).getParameter(0)
)
or
t.start() and
result = jQueryPasteEvent(DataFlow::TypeTracker::end()).getAPropertyRead("originalEvent")
or
exists(DataFlow::TypeTracker t2 | result = pasteEvent(t2).track(t2, t))
}
/**
* Gets a reference to the clipboardData DataTransfer object.
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData
*/
private DataFlow::SourceNode clipboardDataTransferSource(DataFlow::TypeTracker t) {
t.start() and
exists(DataFlow::PropRead read | read = result |
read.getPropertyName() = "clipboardData" and
read.getBase().getALocalSource() = pasteEvent(DataFlow::TypeTracker::end())
)
or
exists(DataFlow::TypeTracker t2 | result = clipboardDataTransferSource(t2).track(t2, t))
}
/**
* A reference to data from the clipboard. Seen as a source for DOM-based XSS.
*/
private class ClipboardSource extends RemoteFlowSource {
ClipboardSource() {
this = clipboardDataTransferSource(DataFlow::TypeTracker::end()).getAMethodCall("getData")
}
override string getSourceType() { result = "Clipboard data" }
}

View File

@@ -0,0 +1,96 @@
/**
* Provides predicates for reasoning about events from the DOM that introduce tainted data.
*/
import javascript
/** Gets the name of a DOM event that might introduce tainted data. */
private string getATaintedDomEvent() { result = ["paste", "drop", "beforeinput"] }
/**
* Gets a jQuery event that might introduce tainted data.
* E.g. `e` in `$("#foo").on("paste", function(e) { ... })`.
*/
private DataFlow::SourceNode taintedJQueryEvent(DataFlow::TypeTracker t, string event) {
t.start() and
exists(DataFlow::CallNode call |
call = JQuery::objectRef().getAMethodCall(["bind", "on", "live", "one", "delegate"]) and
call.getArgument(0).mayHaveStringValue(event) and
event = getATaintedDomEvent()
|
result = call.getCallback(call.getNumArgument() - 1).getParameter(0)
)
or
exists(DataFlow::TypeTracker t2 | result = taintedJQueryEvent(t2, event).track(t2, t))
}
/**
* Gets a DOM event that might introduce tainted data.
* E.g. `e` in `document.addEventListener("paste", e => { ... })`.
*/
private DataFlow::SourceNode taintedEvent(DataFlow::TypeTracker t, string event) {
t.start() and
exists(DataFlow::CallNode call | call = DOM::domValueRef().getAMemberCall("addEventListener") |
call.getArgument(0).mayHaveStringValue(event) and
event = getATaintedDomEvent() and
result = call.getCallback(1).getParameter(0)
)
or
t.start() and
exists(DataFlow::ParameterNode pn |
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent
pn.hasUnderlyingType("ClipboardEvent") and
result = pn and
event = "paste"
or
// https://developer.mozilla.org/en-US/docs/Web/API/DragEvent
pn.hasUnderlyingType("DragEvent") and
result = pn and
event = "drop"
or
// https://developer.mozilla.org/en-US/docs/Web/API/InputEvent
pn.hasUnderlyingType("InputEvent") and
result = pn and
event = "beforeinput"
)
or
t.start() and
exists(DataFlow::PropWrite pw | pw = DOM::domValueRef().getAPropertyWrite() |
pw.getPropertyName() = "on" + event and
event = ["paste", "drop"] and // doesn't work for beforeinput, it's just not part of the API
result = pw.getRhs().getABoundFunctionValue(0).getParameter(0)
)
or
t.start() and
result = taintedJQueryEvent(DataFlow::TypeTracker::end(), event).getAPropertyRead("originalEvent")
or
exists(DataFlow::TypeTracker t2 | result = taintedEvent(t2, event).track(t2, t))
}
/**
* Gets a reference to a DataTransfer object.
* https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent/clipboardData
*/
private DataFlow::SourceNode taintedDataTransfer(DataFlow::TypeTracker t) {
t.start() and
result = taintedEvent(DataFlow::TypeTracker::end(), "paste").getAPropertyRead("clipboardData")
or
t.start() and
result =
taintedEvent(DataFlow::TypeTracker::end(), ["drop", "beforeinput"])
.getAPropertyRead("dataTransfer")
or
exists(DataFlow::TypeTracker t2 | result = taintedDataTransfer(t2).track(t2, t))
}
/**
* A reference to data from a DataTransfer object, which might originate from e.g. the clipboard.
* Seen as a source for DOM-based XSS.
*/
private class TaintedDataTransfer extends RemoteFlowSource {
TaintedDataTransfer() {
this = taintedDataTransfer(DataFlow::TypeTracker::end()).getAMethodCall("getData")
}
override string getSourceType() { result = "Clipboard data" }
}

View File

@@ -1,75 +0,0 @@
/**
* Provides predicates for reasoning about dragAndDrop data.
*/
import javascript
/**
* Gets a jQuery "drop" event.
* E.g. `e` in `$("#foo").on("drop", function(e) { ... })`.
*/
private DataFlow::SourceNode jQueryDropEvent(DataFlow::TypeTracker t) {
t.start() and
exists(DataFlow::CallNode call |
call = JQuery::objectRef().getAMethodCall(["bind", "on", "live", "one", "delegate"]) and
call.getArgument(0).mayHaveStringValue("drop")
|
result = call.getCallback(call.getNumArgument() - 1).getParameter(0)
)
or
exists(DataFlow::TypeTracker t2 | result = jQueryDropEvent(t2).track(t2, t))
}
/**
* Gets a DOM "drop" event.
* E.g. `e` in `document.addEventListener("drop", e => { ... })`.
*/
private DataFlow::SourceNode dropEvent(DataFlow::TypeTracker t) {
t.start() and
exists(DataFlow::CallNode call | call = DOM::domValueRef().getAMemberCall("addEventListener") |
call.getArgument(0).mayHaveStringValue("drop") and
result = call.getCallback(1).getParameter(0)
)
or
t.start() and
exists(DataFlow::ParameterNode pn |
// https://developer.mozilla.org/en-US/docs/Web/API/DragEvent
pn.hasUnderlyingType("DragEvent") and result = pn
)
or
t.start() and
exists(DataFlow::PropWrite pw | pw = DOM::domValueRef().getAPropertyWrite() |
pw.getPropertyName() = "ondrop" and
result = pw.getRhs().getABoundFunctionValue(0).getParameter(0)
)
or
t.start() and
result = jQueryDropEvent(DataFlow::TypeTracker::end()).getAPropertyRead("originalEvent")
or
exists(DataFlow::TypeTracker t2 | result = dropEvent(t2).track(t2, t))
}
/**
* Gets a reference to the dragAndDropData DataTransfer object.
* https://developer.mozilla.org/docs/Web/API/HTML_Drag_and_Drop_API
*/
private DataFlow::SourceNode dragAndDropDataTransferSource(DataFlow::TypeTracker t) {
t.start() and
exists(DataFlow::PropRead read | read = result |
read.getPropertyName() = "dataTransfer" and
read.getBase().getALocalSource() = dropEvent(DataFlow::TypeTracker::end())
)
or
exists(DataFlow::TypeTracker t2 | result = dragAndDropDataTransferSource(t2).track(t2, t))
}
/**
* A reference to data from the dragAndDrop. Seen as a source for DOM-based XSS.
*/
private class DragAndDropSource extends RemoteFlowSource {
DragAndDropSource() {
this = dragAndDropDataTransferSource(DataFlow::TypeTracker::end()).getAMethodCall("getData")
}
override string getSourceType() { result = "DragAndDrop data" }
}

View File

@@ -152,6 +152,14 @@ nodes
| clipboard.ts:73:29:73:39 | droppedHtml |
| clipboard.ts:73:29:73:39 | droppedHtml |
| clipboard.ts:73:29:73:39 | droppedHtml |
| clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') |
| clipboard.ts:98:22:98:54 | dataTra ... /html') |
| clipboard.ts:98:22:98:54 | dataTra ... /html') |
| clipboard.ts:99:23:99:26 | html |
| clipboard.ts:99:23:99:26 | html |
| clipboard.ts:99:23:99:26 | html |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
@@ -1202,6 +1210,14 @@ edges
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
@@ -2164,6 +2180,7 @@ edges
| clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') | clipboard.ts:33:19:33:68 | e.origi ... /html') | Cross-site scripting vulnerability due to $@. | clipboard.ts:33:19:33:68 | e.origi ... /html') | user-provided value |
| clipboard.ts:50:29:50:32 | html | clipboard.ts:43:22:43:55 | clipboa ... /html') | clipboard.ts:50:29:50:32 | html | Cross-site scripting vulnerability due to $@. | clipboard.ts:43:22:43:55 | clipboa ... /html') | user-provided value |
| clipboard.ts:73:29:73:39 | droppedHtml | clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:73:29:73:39 | droppedHtml | Cross-site scripting vulnerability due to $@. | clipboard.ts:71:27:71:62 | e.clipb ... /html') | user-provided value |
| clipboard.ts:99:23:99:26 | html | clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:99:23:99:26 | html | Cross-site scripting vulnerability due to $@. | clipboard.ts:98:22:98:54 | dataTra ... /html') | user-provided value |
| custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name | Cross-site scripting vulnerability due to $@. | custom-element.js:5:26:5:36 | window.name | user-provided value |
| d3.js:11:15:11:24 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |
| d3.js:12:20:12:29 | getTaint() | d3.js:4:12:4:22 | window.name | d3.js:12:20:12:29 | getTaint() | Cross-site scripting vulnerability due to $@. | d3.js:4:12:4:22 | window.name | user-provided value |

View File

@@ -152,6 +152,14 @@ nodes
| clipboard.ts:73:29:73:39 | droppedHtml |
| clipboard.ts:73:29:73:39 | droppedHtml |
| clipboard.ts:73:29:73:39 | droppedHtml |
| clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') |
| clipboard.ts:98:22:98:54 | dataTra ... /html') |
| clipboard.ts:98:22:98:54 | dataTra ... /html') |
| clipboard.ts:99:23:99:26 | html |
| clipboard.ts:99:23:99:26 | html |
| clipboard.ts:99:23:99:26 | html |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
| custom-element.js:5:26:5:36 | window.name |
@@ -1252,6 +1260,14 @@ edges
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
| clipboard.ts:71:27:71:62 | e.clipb ... /html') | clipboard.ts:71:13:71:62 | droppedHtml |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:15:98:54 | html | clipboard.ts:99:23:99:26 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| clipboard.ts:98:22:98:54 | dataTra ... /html') | clipboard.ts:98:15:98:54 | html |
| custom-element.js:5:26:5:36 | window.name | custom-element.js:5:26:5:36 | window.name |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |
| d3.js:4:12:4:22 | window.name | d3.js:11:15:11:24 | getTaint() |

View File

@@ -86,4 +86,16 @@ async function getClipboardData(e: ClipboardEvent): Promise<Array<File | string>
const imageItems = Array.from(dropItems);
return imageItems;
}
}
// inputevent
(function () {
let div = document.createElement("div");
div.addEventListener("beforeinput", function (e: InputEvent) {
const { data, inputType, isComposing, dataTransfer } = e;
if (!dataTransfer) return;
const html = dataTransfer.getData('text/html');
$("#id").html(html); // NOT OK
});
})();