lots of changes
|
After Width: | Height: | Size: 32 KiB |
BIN
docs/codeql/ql-training/_static-training/analysis-overview.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/codeql/ql-training/_static-training/curiosity.png
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
docs/codeql/ql-training/_static-training/curiosity2.png
Normal file
|
After Width: | Height: | Size: 446 KiB |
162
docs/codeql/ql-training/_static-training/end-slide.svg
Normal file
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 292 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 31 KiB |
1
docs/codeql/ql-training/_static-training/setup-slide.svg
Normal file
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,5 @@
|
||||
<slide class="end-slide">
|
||||
<article>
|
||||
</article>
|
||||
</slide>
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
<!--
|
||||
Google IO 2012/2013 HTML5 Slide Template
|
||||
|
||||
Authors: Eric Bidelman <ebidel@gmail.com>
|
||||
Luke Mahé <lukem@google.com>
|
||||
|
||||
URL: https://code.google.com/p/io-2012-slides
|
||||
-->
|
||||
{%- block doctype -%}
|
||||
<!DOCTYPE html>
|
||||
{%- endblock %}
|
||||
|
||||
{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %}
|
||||
{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %}
|
||||
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
|
||||
(sidebars != []) %}
|
||||
{%- set url_root = pathto('', 1) %}
|
||||
{# XXX necessary? #}
|
||||
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
||||
{%- if not embedded and docstitle %}
|
||||
{%- set titlesuffix = " — "|safe + docstitle|e %}
|
||||
{%- else %}
|
||||
{%- set titlesuffix = "" %}
|
||||
{%- endif %}
|
||||
|
||||
{%- macro relbar() %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro sidebar() %}
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro script() %}
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '../',
|
||||
VERSION: '1.21',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true
|
||||
};
|
||||
</script>
|
||||
|
||||
<script data-main="{{ pathto('_static/js/slides', 1) }}"
|
||||
src="{{ pathto('_static/js/require-1.0.8.min.js', 1) }}"></script>
|
||||
|
||||
{%- for scriptfile in script_files %}
|
||||
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
|
||||
{%- endfor %}
|
||||
{% if theme_custom_js %}
|
||||
<script type="text/javascript" src="{{ pathto('_static/' + theme_custom_js, 1) }}"></script>
|
||||
{% endif %}
|
||||
|
||||
{%- endmacro %}
|
||||
|
||||
{%- macro css() %}
|
||||
<link rel="stylesheet" media="all"
|
||||
href="{{ pathto('_static/theme/css/default.css', 1) }}">
|
||||
<link rel="stylesheet" media="all"
|
||||
href="{{ pathto('_static/theme/css/hieroglyph.css', 1) }}">
|
||||
<link rel="stylesheet" media="only screen and (max-device-width: 480px)"
|
||||
href="{{ pathto('_static/theme/css/phone.css', 1) }}">
|
||||
|
||||
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
|
||||
{% if theme_custom_css %}
|
||||
<link rel="stylesheet" href="{{ pathto('_static/' + theme_custom_css, 1) }}"
|
||||
type="text/css" />
|
||||
{% endif %}
|
||||
|
||||
{%- for cssfile in css_files %}
|
||||
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
|
||||
{%- endfor %}
|
||||
{%- endmacro %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
{%- block htmltitle %}
|
||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||
{%- endblock %}
|
||||
<meta charset="{{ encoding }}">
|
||||
{{ metatags }}
|
||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
||||
<!-- comment -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<!--<meta name="viewport" content="width=device-width, initial-scale=1.0">-->
|
||||
<!--This one seems to work all the time, but really small on ipad-->
|
||||
<!--<meta name="viewport" content="initial-scale=0.4">-->
|
||||
<!-- end comment -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<meta name="hieroglyph-title" data-config-title>
|
||||
<meta name="hieroglyph-subtitle" data-config-subtitle>
|
||||
<meta name="hieroglyph-presenter" data-config-presenter>
|
||||
|
||||
{{ css() }}
|
||||
<base target="_blank"> <!-- This amazingness opens all links in a new tab. -->
|
||||
{%- if not embedded %}
|
||||
{{ script() }}
|
||||
{%- if use_opensearch %}
|
||||
<link rel="search" type="application/opensearchdescription+xml"
|
||||
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
|
||||
href="{{ pathto('_static/opensearch.xml', 1) }}"/>
|
||||
{%- endif %}
|
||||
{%- if favicon %}
|
||||
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- block linktags %}
|
||||
{%- if hasdoc('about') %}
|
||||
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('genindex') %}
|
||||
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('search') %}
|
||||
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
|
||||
{%- endif %}
|
||||
{%- if hasdoc('copyright') %}
|
||||
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
|
||||
{%- endif %}
|
||||
<link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}" />
|
||||
{%- if parents %}
|
||||
<link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}" />
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
|
||||
{%- endif %}
|
||||
{%- if prev %}
|
||||
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
|
||||
{%- endif %}
|
||||
{%- endblock %}
|
||||
{%- block extrahead %}
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato&display=swap" rel="stylesheet">
|
||||
<link href='https://fonts.googleapis.com/css?family=Work+Sans&display=swap' rel='stylesheet'>
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body style="opacity: 0">
|
||||
|
||||
<slides class="layout-widescreen" id="slides">
|
||||
|
||||
<!-- {% include "title_slide.html" %} -->
|
||||
|
||||
{% block body %}{% endblock %}
|
||||
|
||||
{% include "end_slide.html" %}
|
||||
|
||||
<slide class="backdrop"></slide>
|
||||
|
||||
</slides>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
//insert info buttons on slides that have additional notes
|
||||
$(".admonition.note").before("<button id='extra-notes'>ⓘ</button>");
|
||||
$(".admonition-title").before("<button id='close-notes'>×</button>");
|
||||
$(document).ready(function() {
|
||||
$('button').click(function() {
|
||||
document.body.classList.toggle('with-notes');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
//assigns font-size when document is ready
|
||||
document.onreadystatechange = () => {
|
||||
if (document.readyState === 'complete') {
|
||||
var wrapperHeight = document.getElementById('slides').clientHeight;
|
||||
var relativeFontSize = wrapperHeight / 45 + 'px'; //change integer to set desired font size
|
||||
document.getElementById("slides").style.fontSize = relativeFontSize;
|
||||
}
|
||||
};
|
||||
//then on window resize
|
||||
window.onresize = function(event) {
|
||||
var wrapperHeight = document.getElementById('slides').clientHeight;
|
||||
var relativeFontSize = wrapperHeight / 45 + 'px'; //change integer to set for desired font size
|
||||
document.getElementById("slides").style.fontSize = relativeFontSize;
|
||||
};
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<slide class="{% if slide_number==1 %}title-slide segue nobackground {% endif %} {% if slide_classes %}{{ ' '.join(slide_classes) }} {% endif %}level-{{ level }}"{% if id %} id="{{ id }}"{% endif %}>
|
||||
|
||||
<hgroup>
|
||||
<h{{ level }}>{{ title }}</h{{ level }}>
|
||||
</hgroup>
|
||||
|
||||
<article class="{{ content_classes|join(' ') }}">
|
||||
|
||||
{{ content }}
|
||||
|
||||
|
||||
{% if config.slide_numbers %}
|
||||
<div class="slide-no">{{ slide_number }}</div>
|
||||
{% endif %}
|
||||
{% if config.slide_footer %}
|
||||
<div class="slide-footer">{{ config.slide_footer }}</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
</slide>
|
||||
@@ -0,0 +1,24 @@
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "theme/css"
|
||||
sass_dir = "theme/scss"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "js"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
output_style = :expanded #:expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
|
||||
# If you prefer the indented syntax, you might want to regenerate this
|
||||
# project again passing --syntax sass, or you can uncomment this:
|
||||
# preferred_syntax = :sass
|
||||
# and then run:
|
||||
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
|
||||
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
* Hammer.JS
|
||||
* version 0.4
|
||||
* author: Eight Media
|
||||
* https://github.com/EightMedia/hammer.js
|
||||
*/
|
||||
function Hammer(element, options, undefined)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
var defaults = {
|
||||
// prevent the default event or not... might be buggy when false
|
||||
prevent_default : false,
|
||||
css_hacks : true,
|
||||
|
||||
drag : true,
|
||||
drag_vertical : true,
|
||||
drag_horizontal : true,
|
||||
// minimum distance before the drag event starts
|
||||
drag_min_distance : 20, // pixels
|
||||
|
||||
// pinch zoom and rotation
|
||||
transform : true,
|
||||
scale_treshold : 0.1,
|
||||
rotation_treshold : 15, // degrees
|
||||
|
||||
tap : true,
|
||||
tap_double : true,
|
||||
tap_max_interval : 300,
|
||||
tap_double_distance: 20,
|
||||
|
||||
hold : true,
|
||||
hold_timeout : 500
|
||||
};
|
||||
options = mergeObject(defaults, options);
|
||||
|
||||
// some css hacks
|
||||
(function() {
|
||||
if(!options.css_hacks) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var vendors = ['webkit','moz','ms','o',''];
|
||||
var css_props = {
|
||||
"userSelect": "none",
|
||||
"touchCallout": "none",
|
||||
"userDrag": "none",
|
||||
"tapHighlightColor": "rgba(0,0,0,0)"
|
||||
};
|
||||
|
||||
var prop = '';
|
||||
for(var i = 0; i < vendors.length; i++) {
|
||||
for(var p in css_props) {
|
||||
prop = p;
|
||||
if(vendors[i]) {
|
||||
prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
|
||||
}
|
||||
element.style[ prop ] = css_props[p];
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
// holds the distance that has been moved
|
||||
var _distance = 0;
|
||||
|
||||
// holds the exact angle that has been moved
|
||||
var _angle = 0;
|
||||
|
||||
// holds the diraction that has been moved
|
||||
var _direction = 0;
|
||||
|
||||
// holds position movement for sliding
|
||||
var _pos = { };
|
||||
|
||||
// how many fingers are on the screen
|
||||
var _fingers = 0;
|
||||
|
||||
var _first = false;
|
||||
|
||||
var _gesture = null;
|
||||
var _prev_gesture = null;
|
||||
|
||||
var _touch_start_time = null;
|
||||
var _prev_tap_pos = {x: 0, y: 0};
|
||||
var _prev_tap_end_time = null;
|
||||
|
||||
var _hold_timer = null;
|
||||
|
||||
var _offset = {};
|
||||
|
||||
// keep track of the mouse status
|
||||
var _mousedown = false;
|
||||
|
||||
var _event_start;
|
||||
var _event_move;
|
||||
var _event_end;
|
||||
|
||||
|
||||
/**
|
||||
* angle to direction define
|
||||
* @param float angle
|
||||
* @return string direction
|
||||
*/
|
||||
this.getDirectionFromAngle = function( angle )
|
||||
{
|
||||
var directions = {
|
||||
down: angle >= 45 && angle < 135, //90
|
||||
left: angle >= 135 || angle <= -135, //180
|
||||
up: angle < -45 && angle > -135, //270
|
||||
right: angle >= -45 && angle <= 45 //0
|
||||
};
|
||||
|
||||
var direction, key;
|
||||
for(key in directions){
|
||||
if(directions[key]){
|
||||
direction = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return direction;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* count the number of fingers in the event
|
||||
* when no fingers are detected, one finger is returned (mouse pointer)
|
||||
* @param event
|
||||
* @return int fingers
|
||||
*/
|
||||
function countFingers( event )
|
||||
{
|
||||
// there is a bug on android (until v4?) that touches is always 1,
|
||||
// so no multitouch is supported, e.g. no, zoom and rotation...
|
||||
return event.touches ? event.touches.length : 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the x and y positions from the event object
|
||||
* @param event
|
||||
* @return array [{ x: int, y: int }]
|
||||
*/
|
||||
function getXYfromEvent( event )
|
||||
{
|
||||
event = event || window.event;
|
||||
|
||||
// no touches, use the event pageX and pageY
|
||||
if(!event.touches) {
|
||||
var doc = document,
|
||||
body = doc.body;
|
||||
|
||||
return [{
|
||||
x: event.pageX || event.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && doc.clientLeft || 0 ),
|
||||
y: event.pageY || event.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && doc.clientTop || 0 )
|
||||
}];
|
||||
}
|
||||
// multitouch, return array with positions
|
||||
else {
|
||||
var pos = [], src;
|
||||
for(var t=0, len=event.touches.length; t<len; t++) {
|
||||
src = event.touches[t];
|
||||
pos.push({ x: src.pageX, y: src.pageY });
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* calculate the angle between two points
|
||||
* @param object pos1 { x: int, y: int }
|
||||
* @param object pos2 { x: int, y: int }
|
||||
*/
|
||||
function getAngle( pos1, pos2 )
|
||||
{
|
||||
return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
/**
|
||||
* trigger an event/callback by name with params
|
||||
* @param string name
|
||||
* @param array params
|
||||
*/
|
||||
function triggerEvent( eventName, params )
|
||||
{
|
||||
// return touches object
|
||||
params.touches = getXYfromEvent(params.originalEvent);
|
||||
params.type = eventName;
|
||||
|
||||
// trigger callback
|
||||
if(isFunction(self["on"+ eventName])) {
|
||||
self["on"+ eventName].call(self, params);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cancel event
|
||||
* @param object event
|
||||
* @return void
|
||||
*/
|
||||
|
||||
function cancelEvent(event){
|
||||
event = event || window.event;
|
||||
if(event.preventDefault){
|
||||
event.preventDefault();
|
||||
}else{
|
||||
event.returnValue = false;
|
||||
event.cancelBubble = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* reset the internal vars to the start values
|
||||
*/
|
||||
function reset()
|
||||
{
|
||||
_pos = {};
|
||||
_first = false;
|
||||
_fingers = 0;
|
||||
_distance = 0;
|
||||
_angle = 0;
|
||||
_gesture = null;
|
||||
}
|
||||
|
||||
|
||||
var gestures = {
|
||||
// hold gesture
|
||||
// fired on touchstart
|
||||
hold : function(event)
|
||||
{
|
||||
// only when one finger is on the screen
|
||||
if(options.hold) {
|
||||
_gesture = 'hold';
|
||||
clearTimeout(_hold_timer);
|
||||
|
||||
_hold_timer = setTimeout(function() {
|
||||
if(_gesture == 'hold') {
|
||||
triggerEvent("hold", {
|
||||
originalEvent : event,
|
||||
position : _pos.start
|
||||
});
|
||||
}
|
||||
}, options.hold_timeout);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// drag gesture
|
||||
// fired on mousemove
|
||||
drag : function(event)
|
||||
{
|
||||
// get the distance we moved
|
||||
var _distance_x = _pos.move[0].x - _pos.start[0].x;
|
||||
var _distance_y = _pos.move[0].y - _pos.start[0].y;
|
||||
_distance = Math.sqrt(_distance_x * _distance_x + _distance_y * _distance_y);
|
||||
|
||||
// drag
|
||||
// minimal movement required
|
||||
if(options.drag && (_distance > options.drag_min_distance) || _gesture == 'drag') {
|
||||
// calculate the angle
|
||||
_angle = getAngle(_pos.start[0], _pos.move[0]);
|
||||
_direction = self.getDirectionFromAngle(_angle);
|
||||
|
||||
// check the movement and stop if we go in the wrong direction
|
||||
var is_vertical = (_direction == 'up' || _direction == 'down');
|
||||
if(((is_vertical && !options.drag_vertical) || (!is_vertical && !options.drag_horizontal))
|
||||
&& (_distance > options.drag_min_distance)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_gesture = 'drag';
|
||||
|
||||
var position = { x: _pos.move[0].x - _offset.left,
|
||||
y: _pos.move[0].y - _offset.top };
|
||||
|
||||
var event_obj = {
|
||||
originalEvent : event,
|
||||
position : position,
|
||||
direction : _direction,
|
||||
distance : _distance,
|
||||
distanceX : _distance_x,
|
||||
distanceY : _distance_y,
|
||||
angle : _angle
|
||||
};
|
||||
|
||||
// on the first time trigger the start event
|
||||
if(_first) {
|
||||
triggerEvent("dragstart", event_obj);
|
||||
|
||||
_first = false;
|
||||
}
|
||||
|
||||
// normal slide event
|
||||
triggerEvent("drag", event_obj);
|
||||
|
||||
cancelEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// transform gesture
|
||||
// fired on touchmove
|
||||
transform : function(event)
|
||||
{
|
||||
if(options.transform) {
|
||||
var scale = event.scale || 1;
|
||||
var rotation = event.rotation || 0;
|
||||
|
||||
if(countFingers(event) != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_gesture != 'drag' &&
|
||||
(_gesture == 'transform' || Math.abs(1-scale) > options.scale_treshold
|
||||
|| Math.abs(rotation) > options.rotation_treshold)) {
|
||||
_gesture = 'transform';
|
||||
|
||||
_pos.center = { x: ((_pos.move[0].x + _pos.move[1].x) / 2) - _offset.left,
|
||||
y: ((_pos.move[0].y + _pos.move[1].y) / 2) - _offset.top };
|
||||
|
||||
var event_obj = {
|
||||
originalEvent : event,
|
||||
position : _pos.center,
|
||||
scale : scale,
|
||||
rotation : rotation
|
||||
};
|
||||
|
||||
// on the first time trigger the start event
|
||||
if(_first) {
|
||||
triggerEvent("transformstart", event_obj);
|
||||
_first = false;
|
||||
}
|
||||
|
||||
triggerEvent("transform", event_obj);
|
||||
|
||||
cancelEvent(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
|
||||
// tap and double tap gesture
|
||||
// fired on touchend
|
||||
tap : function(event)
|
||||
{
|
||||
// compare the kind of gesture by time
|
||||
var now = new Date().getTime();
|
||||
var touch_time = now - _touch_start_time;
|
||||
|
||||
// dont fire when hold is fired
|
||||
if(options.hold && !(options.hold && options.hold_timeout > touch_time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// when previous event was tap and the tap was max_interval ms ago
|
||||
var is_double_tap = (function(){
|
||||
if (_prev_tap_pos && options.tap_double && _prev_gesture == 'tap' && (_touch_start_time - _prev_tap_end_time) < options.tap_max_interval) {
|
||||
var x_distance = Math.abs(_prev_tap_pos[0].x - _pos.start[0].x);
|
||||
var y_distance = Math.abs(_prev_tap_pos[0].y - _pos.start[0].y);
|
||||
return (_prev_tap_pos && _pos.start && Math.max(x_distance, y_distance) < options.tap_double_distance);
|
||||
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
if(is_double_tap) {
|
||||
_gesture = 'double_tap';
|
||||
_prev_tap_end_time = null;
|
||||
|
||||
triggerEvent("doubletap", {
|
||||
originalEvent : event,
|
||||
position : _pos.start
|
||||
});
|
||||
cancelEvent(event);
|
||||
}
|
||||
|
||||
// single tap is single touch
|
||||
else {
|
||||
_gesture = 'tap';
|
||||
_prev_tap_end_time = now;
|
||||
_prev_tap_pos = _pos.start;
|
||||
|
||||
if(options.tap) {
|
||||
triggerEvent("tap", {
|
||||
originalEvent : event,
|
||||
position : _pos.start
|
||||
});
|
||||
cancelEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
function handleEvents(event)
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case 'mousedown':
|
||||
case 'touchstart':
|
||||
_pos.start = getXYfromEvent(event);
|
||||
_touch_start_time = new Date().getTime();
|
||||
_fingers = countFingers(event);
|
||||
_first = true;
|
||||
_event_start = event;
|
||||
|
||||
// borrowed from jquery offset https://github.com/jquery/jquery/blob/master/src/offset.js
|
||||
var box = element.getBoundingClientRect();
|
||||
var clientTop = element.clientTop || document.body.clientTop || 0;
|
||||
var clientLeft = element.clientLeft || document.body.clientLeft || 0;
|
||||
var scrollTop = window.pageYOffset || element.scrollTop || document.body.scrollTop;
|
||||
var scrollLeft = window.pageXOffset || element.scrollLeft || document.body.scrollLeft;
|
||||
|
||||
_offset = {
|
||||
top: box.top + scrollTop - clientTop,
|
||||
left: box.left + scrollLeft - clientLeft
|
||||
};
|
||||
|
||||
_mousedown = true;
|
||||
|
||||
// hold gesture
|
||||
gestures.hold(event);
|
||||
|
||||
if(options.prevent_default) {
|
||||
cancelEvent(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mousemove':
|
||||
case 'touchmove':
|
||||
if(!_mousedown) {
|
||||
return false;
|
||||
}
|
||||
_event_move = event;
|
||||
_pos.move = getXYfromEvent(event);
|
||||
|
||||
if(!gestures.transform(event)) {
|
||||
gestures.drag(event);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mouseup':
|
||||
case 'mouseout':
|
||||
case 'touchcancel':
|
||||
case 'touchend':
|
||||
if(!_mousedown || (_gesture != 'transform' && event.touches && event.touches.length > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_mousedown = false;
|
||||
_event_end = event;
|
||||
|
||||
// drag gesture
|
||||
// dragstart is triggered, so dragend is possible
|
||||
if(_gesture == 'drag') {
|
||||
triggerEvent("dragend", {
|
||||
originalEvent : event,
|
||||
direction : _direction,
|
||||
distance : _distance,
|
||||
angle : _angle
|
||||
});
|
||||
}
|
||||
|
||||
// transform
|
||||
// transformstart is triggered, so transformed is possible
|
||||
else if(_gesture == 'transform') {
|
||||
triggerEvent("transformend", {
|
||||
originalEvent : event,
|
||||
position : _pos.center,
|
||||
scale : event.scale,
|
||||
rotation : event.rotation
|
||||
});
|
||||
}
|
||||
else {
|
||||
gestures.tap(_event_start);
|
||||
}
|
||||
|
||||
_prev_gesture = _gesture;
|
||||
|
||||
// reset vars
|
||||
reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// bind events for touch devices
|
||||
// except for windows phone 7.5, it doesnt support touch events..!
|
||||
if('ontouchstart' in window) {
|
||||
element.addEventListener("touchstart", handleEvents, false);
|
||||
element.addEventListener("touchmove", handleEvents, false);
|
||||
element.addEventListener("touchend", handleEvents, false);
|
||||
element.addEventListener("touchcancel", handleEvents, false);
|
||||
}
|
||||
// for non-touch
|
||||
else {
|
||||
|
||||
if(element.addEventListener){ // prevent old IE errors
|
||||
element.addEventListener("mouseout", function(event) {
|
||||
if(!isInsideHammer(element, event.relatedTarget)) {
|
||||
handleEvents(event);
|
||||
}
|
||||
}, false);
|
||||
element.addEventListener("mouseup", handleEvents, false);
|
||||
element.addEventListener("mousedown", handleEvents, false);
|
||||
element.addEventListener("mousemove", handleEvents, false);
|
||||
|
||||
// events for older IE
|
||||
}else if(document.attachEvent){
|
||||
element.attachEvent("onmouseout", function(event) {
|
||||
if(!isInsideHammer(element, event.relatedTarget)) {
|
||||
handleEvents(event);
|
||||
}
|
||||
}, false);
|
||||
element.attachEvent("onmouseup", handleEvents);
|
||||
element.attachEvent("onmousedown", handleEvents);
|
||||
element.attachEvent("onmousemove", handleEvents);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* find if element is (inside) given parent element
|
||||
* @param object element
|
||||
* @param object parent
|
||||
* @return bool inside
|
||||
*/
|
||||
function isInsideHammer(parent, child) {
|
||||
// get related target for IE
|
||||
if(!child && window.event && window.event.toElement){
|
||||
child = window.event.toElement;
|
||||
}
|
||||
|
||||
if(parent === child){
|
||||
return true;
|
||||
}
|
||||
|
||||
// loop over parentNodes of child until we find hammer element
|
||||
if(child){
|
||||
var node = child.parentNode;
|
||||
while(node !== null){
|
||||
if(node === parent){
|
||||
return true;
|
||||
};
|
||||
node = node.parentNode;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* merge 2 objects into a new object
|
||||
* @param object obj1
|
||||
* @param object obj2
|
||||
* @return object merged object
|
||||
*/
|
||||
function mergeObject(obj1, obj2) {
|
||||
var output = {};
|
||||
|
||||
if(!obj2) {
|
||||
return obj1;
|
||||
}
|
||||
|
||||
for (var prop in obj1) {
|
||||
if (prop in obj2) {
|
||||
output[prop] = obj2[prop];
|
||||
} else {
|
||||
output[prop] = obj1[prop];
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function isFunction( obj ){
|
||||
return Object.prototype.toString.call( obj ) == "[object Function]";
|
||||
}
|
||||
}
|
||||
4
docs/codeql/ql-training/_static-training/slides-semmle-2/static/js/modernizr.custom.45394.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
RequireJS order 1.0.5 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.
|
||||
Available via the MIT or new BSD license.
|
||||
see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
(function(){function k(a){var b=a.currentTarget||a.srcElement,c;if(a.type==="load"||l.test(b.readyState)){a=b.getAttribute("data-requiremodule");j[a]=!0;for(a=0;c=g[a];a++)if(j[c.name])c.req([c.name],c.onLoad);else break;a>0&&g.splice(0,a);setTimeout(function(){b.parentNode.removeChild(b)},15)}}function m(a){var b,c;a.setAttribute("data-orderloaded","loaded");for(a=0;c=h[a];a++)if((b=i[c])&&b.getAttribute("data-orderloaded")==="loaded")delete i[c],require.addScriptToDom(b);else break;a>0&&h.splice(0,
|
||||
a)}var f=typeof document!=="undefined"&&typeof window!=="undefined"&&document.createElement("script"),n=f&&(f.async||window.opera&&Object.prototype.toString.call(window.opera)==="[object Opera]"||"MozAppearance"in document.documentElement.style),o=f&&f.readyState==="uninitialized",l=/^(complete|loaded)$/,g=[],j={},i={},h=[],f=null;define({version:"1.0.5",load:function(a,b,c,e){var d;b.nameToUrl?(d=b.nameToUrl(a,null),require.s.skipAsync[d]=!0,n||e.isBuild?b([a],c):o?(e=require.s.contexts._,!e.urlFetched[d]&&
|
||||
!e.loaded[a]&&(e.urlFetched[d]=!0,require.resourcesReady(!1),e.scriptCount+=1,d=require.attach(d,e,a,null,null,m),i[a]=d,h.push(a)),b([a],c)):b.specified(a)?b([a],c):(g.push({name:a,req:b,onLoad:c}),require.attach(d,null,a,k,"script/cache"))):b([a],c)}})})();
|
||||
@@ -0,0 +1,2 @@
|
||||
/* @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
|
||||
"use strict";if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(a){var f="classList",d="prototype",e=(a.HTMLElement||a.Element)[d],g=Object;strTrim=String[d].trim||function(){return this.replace(/^\s+|\s+$/g,"")},arrIndexOf=Array[d].indexOf||function(k){for(var j=0,h=this.length;j<h;j++){if(j in this&&this[j]===k){return j}}return -1},DOMEx=function(h,i){this.name=h;this.code=DOMException[h];this.message=i},checkTokenAndGetIndex=function(i,h){if(h===""){throw new DOMEx("SYNTAX_ERR","An invalid or illegal string was specified")}if(/\s/.test(h)){throw new DOMEx("INVALID_CHARACTER_ERR","String contains an invalid character")}return arrIndexOf.call(i,h)},ClassList=function(m){var l=strTrim.call(m.className),k=l?l.split(/\s+/):[];for(var j=0,h=k.length;j<h;j++){this.push(k[j])}this._updateClassName=function(){m.className=this.toString()}},classListProto=ClassList[d]=[],classListGetter=function(){return new ClassList(this)};DOMEx[d]=Error[d];classListProto.item=function(h){return this[h]||null};classListProto.contains=function(h){h+="";return checkTokenAndGetIndex(this,h)!==-1};classListProto.add=function(h){h+="";if(checkTokenAndGetIndex(this,h)===-1){this.push(h);this._updateClassName()}};classListProto.remove=function(i){i+="";var h=checkTokenAndGetIndex(this,i);if(h!==-1){this.splice(h,1);this._updateClassName()}};classListProto.toggle=function(h){h+="";if(checkTokenAndGetIndex(this,h)===-1){this.add(h)}else{this.remove(h)}};classListProto.toString=function(){return this.join(" ")};if(g.defineProperty){var c={get:classListGetter,enumerable:true,configurable:true};try{g.defineProperty(e,f,c)}catch(b){if(b.number===-2146823252){c.enumerable=false;g.defineProperty(e,f,c)}}}else{if(g[d].__defineGetter__){e.__defineGetter__(f,classListGetter)}}}(self))};
|
||||
2
docs/codeql/ql-training/_static-training/slides-semmle-2/static/js/polyfills/dataset.min.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
(function(){function c(){d=!0;this.removeEventListener("DOMAttrModified",c,!1)}function g(b){return b.replace(h,function(b,a){return a.toUpperCase()})}function e(){var b={};i.call(this.attributes,function(a){if(f=a.name.match(j))b[g(f[1])]=a.value});return b}var i=[].forEach,j=/^data-(.+)/,h=/\-([a-z])/ig,a=document.createElement("div"),d=!1,f;a.dataset==void 0&&(a.addEventListener("DOMAttrModified",c,!1),a.setAttribute("foo","bar"),Element.prototype.__defineGetter__("dataset",d?function(){if(!this._datasetCache)this._datasetCache=
|
||||
e.call(this);return this._datasetCache}:e),document.addEventListener("DOMAttrModified",function(a){delete a.target._datasetCache},!1))})();
|
||||
1
docs/codeql/ql-training/_static-training/slides-semmle-2/static/js/polyfills/history.min.js
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\n\r]*/,null,"#"],["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
|
||||
null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[ES]?BANK=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[!-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["apollo","agc","aea"]);
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright (C) 2011 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
var a=null;
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a],
|
||||
["typ",/^:[\dA-Za-z-]+/]]),["clj"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
|
||||
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
|
||||
@@ -0,0 +1 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["pln",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])+(?:'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\/\*[\S\s]*?\*\/)/],["pln",/^(?:[^"'/`]|\/(?![*/]))+/]]),["go"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t-\r ]+/,null,"\t\n\r "],["str",/^"(?:[^\n\f\r"\\]|\\[\S\s])*(?:"|$)/,null,'"'],["str",/^'(?:[^\n\f\r'\\]|\\[^&])'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:--+[^\n\f\r]*|{-(?:[^-]|-+[^}-])*-})/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^\d'A-Za-z]|$)/,
|
||||
null],["pln",/^(?:[A-Z][\w']*\.)*[A-Za-z][\w']*/],["pun",/^[^\d\t-\r "'A-Za-z]+/]]),["hs"]);
|
||||
@@ -0,0 +1,3 @@
|
||||
var a=null;
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,a,"("],["clo",/^\)+/,a,")"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,a],
|
||||
["lit",/^[+-]?(?:[#0]x[\da-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[de][+-]?\d+)?)/i],["lit",/^'(?:-*(?:\w|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?)?/],["pln",/^-*(?:[_a-z]|\\[!-~])(?:[\w-]*|\\[!-~])[!=?]?/i],["pun",/^[^\w\t\n\r "'-);\\\xa0]+/]]),["cl","el","lisp","scm"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\S\s]*?(?:]\1]|$)|[^\n\r]*)/],["str",/^\[(=*)\[[\S\s]*?(?:]\1]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],
|
||||
["pln",/^[_a-z]\w*/i],["pun",/^[^\w\t\n\r \xa0][^\w\t\n\r "'+=\xa0-]*/]]),["lua"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["com",/^#(?:if[\t\n\r \xa0]+(?:[$_a-z][\w']*|``[^\t\n\r`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:"(?:[^"\\]|\\[\S\s])*(?:"|$)|'(?:[^'\\]|\\[\S\s])(?:'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\n\r]*|\(\*[\S\s]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
|
||||
["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^(?:[_a-z][\w']*[!#?]?|``[^\t\n\r`]*(?:``|$))/i],["pun",/^[^\w\t\n\r "'\xa0]+/]]),["fs","ml"]);
|
||||
@@ -0,0 +1,4 @@
|
||||
var a=null;
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:'(?:[^\n\r'\\]|\\.)*'|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,a,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,a,"#"],["pln",/^\s+/,a," \r\n\t\xa0"]],[["str",/^@"(?:[^"]|"")*(?:"|$)/,a],["str",/^<#[^#>]*(?:#>|$)/,a],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,a],["com",/^\/\/[^\n\r]*/,a],["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
|
||||
a],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
|
||||
a],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,a],["lit",/^@[$_a-z][\w$@]*/i,a],["typ",/^@[A-Z]+[a-z][\w$@]*/,a],["pln",/^'?[$_a-z][\w$@]*/i,a],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,a,"0123456789"],["pun",/^.[^\s\w"-$'./@`]*/,a]]),["n","nemerle"]);
|
||||
@@ -0,0 +1 @@
|
||||
PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/],
|
||||
["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i],["typ",/^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/],["pln",/^[$A-Z_a-z][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|merge|national|nocheck|nonclustered|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|percent|plan|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rule|save|schema|select|session_user|set|setuser|shutdown|some|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|union|unique|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|writetext)(?=[^\w-]|$)/i,
|
||||
null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^[_a-z][\w-]*/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/]]),["sql"]);
|
||||
@@ -0,0 +1 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"],["com",/^%[^\n\r]*/,null,"%"]],[["kwd",/^\\[@-Za-z]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[()=[\]{}]+/]]),["latex","tex"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0\u2028\u2029]+/,null,"\t\n\r <20>\xa0
"],["str",/^(?:["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})(?:["\u201c\u201d]c|$)|["\u201c\u201d](?:[^"\u201c\u201d]|["\u201c\u201d]{2})*(?:["\u201c\u201d]|$))/i,null,'"“”'],["com",/^['\u2018\u2019].*/,null,"'‘’"]],[["kwd",/^(?:addhandler|addressof|alias|and|andalso|ansi|as|assembly|auto|boolean|byref|byte|byval|call|case|catch|cbool|cbyte|cchar|cdate|cdbl|cdec|char|cint|class|clng|cobj|const|cshort|csng|cstr|ctype|date|decimal|declare|default|delegate|dim|directcast|do|double|each|else|elseif|end|endif|enum|erase|error|event|exit|finally|for|friend|function|get|gettype|gosub|goto|handles|if|implements|imports|in|inherits|integer|interface|is|let|lib|like|long|loop|me|mod|module|mustinherit|mustoverride|mybase|myclass|namespace|new|next|not|notinheritable|notoverridable|object|on|option|optional|or|orelse|overloads|overridable|overrides|paramarray|preserve|private|property|protected|public|raiseevent|readonly|redim|removehandler|resume|return|select|set|shadows|shared|short|single|static|step|stop|string|structure|sub|synclock|then|throw|to|try|typeof|unicode|until|variant|wend|when|while|with|withevents|writeonly|xor|endif|gosub|let|variant|wend)\b/i,
|
||||
null],["com",/^rem.*/i],["lit",/^(?:true\b|false\b|nothing\b|\d+(?:e[+-]?\d+[dfr]?|[dfilrs])?|(?:&h[\da-f]+|&o[0-7]+)[ils]?|\d*\.\d+(?:e[+-]?\d+)?[dfr]?|#\s+(?:\d+[/-]\d+[/-]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:am|pm))?)?|\d+:\d+(?::\d+)?(\s*(?:am|pm))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*|\[(?:[a-z]|_\w)\w*])/i],["pun",/^[^\w\t\n\r "'[\]\xa0\u2018\u2019\u201c\u201d\u2028\u2029]+/],["pun",/^(?:\[|])/]]),["vb","vbs"]);
|
||||
@@ -0,0 +1,3 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r <20>\xa0"]],[["str",/^(?:[box]?"(?:[^"]|"")*"|'.')/i],["com",/^--[^\n\r]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
|
||||
null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^'(?:active|ascending|base|delayed|driving|driving_value|event|high|image|instance_name|last_active|last_event|last_value|left|leftof|length|low|path_name|pos|pred|quiet|range|reverse_range|right|rightof|simple_name|stable|succ|transaction|val|value)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w.\\]+#(?:[+-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:e[+-]?\d+(?:_\d+)*)?)/i],
|
||||
["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'\xa0-]*/]]),["vhdl","vhd"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\d\t a-gi-z\xa0]+/,null,"\t <20>\xa0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[*=[\]^~]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^[A-Z][a-z][\da-z]+[A-Z][a-z][^\W_]+\b/],["lang-",/^{{{([\S\s]+?)}}}/],["lang-",/^`([^\n\r`]+)`/],["str",/^https?:\/\/[^\s#/?]*(?:\/[^\s#?]*)?(?:\?[^\s#]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\S\s])[^\n\r#*=A-[^`h{~]*/]]),["wiki"]);
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]);
|
||||
@@ -0,0 +1,2 @@
|
||||
var a=null;
|
||||
PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:>?|]+/,a,":|>?"],["dec",/^%(?:YAML|TAG)[^\n\r#]+/,a,"%"],["typ",/^&\S+/,a,"&"],["typ",/^!\S*/,a,"!"],["str",/^"(?:[^"\\]|\\.)*(?:"|$)/,a,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,a,"'"],["com",/^#[^\n\r]*/,a,"#"],["pln",/^\s+/,a," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\n\r]|$)/],["pun",/^-/],["kwd",/^\w+:[\n\r ]/],["pln",/^\w+/]]),["yaml","yml"]);
|
||||
@@ -0,0 +1 @@
|
||||
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
|
||||
@@ -0,0 +1,28 @@
|
||||
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
|
||||
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
|
||||
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
|
||||
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
|
||||
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
|
||||
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
|
||||
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
|
||||
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
|
||||
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
|
||||
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
|
||||
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
|
||||
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
|
||||
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
|
||||
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
|
||||
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
|
||||
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
|
||||
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
|
||||
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
|
||||
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
|
||||
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
|
||||
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
|
||||
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
|
||||
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
|
||||
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
|
||||
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
|
||||
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
|
||||
33
docs/codeql/ql-training/_static-training/slides-semmle-2/static/js/require-1.0.8.min.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
RequireJS 1.0.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
||||
Available via the MIT or new BSD license.
|
||||
see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
var requirejs,require,define;
|
||||
(function(r){function K(a){return O.call(a)==="[object Function]"}function G(a){return O.call(a)==="[object Array]"}function $(a,c,l){for(var j in c)if(!(j in L)&&(!(j in a)||l))a[j]=c[j];return d}function P(a,c,d){a=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+a);if(d)a.originalError=d;return a}function aa(a,c,d){var j,k,t;for(j=0;t=c[j];j++){t=typeof t==="string"?{name:t}:t;k=t.location;if(d&&(!k||k.indexOf("/")!==0&&k.indexOf(":")===-1))k=d+"/"+(k||t.name);a[t.name]={name:t.name,location:k||
|
||||
t.name,main:(t.main||"main").replace(fa,"").replace(ba,"")}}}function V(a,c){a.holdReady?a.holdReady(c):c?a.readyWait+=1:a.ready(!0)}function ga(a){function c(b,f){var g,m;if(b&&b.charAt(0)===".")if(f){q.pkgs[f]?f=[f]:(f=f.split("/"),f=f.slice(0,f.length-1));g=b=f.concat(b.split("/"));var a;for(m=0;a=g[m];m++)if(a===".")g.splice(m,1),m-=1;else if(a==="..")if(m===1&&(g[2]===".."||g[0]===".."))break;else m>0&&(g.splice(m-1,2),m-=2);m=q.pkgs[g=b[0]];b=b.join("/");m&&b===g+"/"+m.main&&(b=g)}else b.indexOf("./")===
|
||||
0&&(b=b.substring(2));return b}function l(b,f){var g=b?b.indexOf("!"):-1,m=null,a=f?f.name:null,h=b,e,d;g!==-1&&(m=b.substring(0,g),b=b.substring(g+1,b.length));m&&(m=c(m,a));b&&(m?e=(g=n[m])&&g.normalize?g.normalize(b,function(b){return c(b,a)}):c(b,a):(e=c(b,a),d=G[e],d||(d=i.nameToUrl(b,null,f),G[e]=d)));return{prefix:m,name:e,parentMap:f,url:d,originalName:h,fullName:m?m+"!"+(e||""):e}}function j(){var b=!0,f=q.priorityWait,g,a;if(f){for(a=0;g=f[a];a++)if(!s[g]){b=!1;break}b&&delete q.priorityWait}return b}
|
||||
function k(b,f,g){return function(){var a=ha.call(arguments,0),c;if(g&&K(c=a[a.length-1]))c.__requireJsBuild=!0;a.push(f);return b.apply(null,a)}}function t(b,f,g){f=k(g||i.require,b,f);$(f,{nameToUrl:k(i.nameToUrl,b),toUrl:k(i.toUrl,b),defined:k(i.requireDefined,b),specified:k(i.requireSpecified,b),isBrowser:d.isBrowser});return f}function p(b){var f,g,a,c=b.callback,h=b.map,e=h.fullName,ca=b.deps;a=b.listeners;var j=q.requireExecCb||d.execCb;if(c&&K(c)){if(q.catchError.define)try{g=j(e,b.callback,
|
||||
ca,n[e])}catch(k){f=k}else g=j(e,b.callback,ca,n[e]);if(e)(c=b.cjsModule)&&c.exports!==r&&c.exports!==n[e]?g=n[e]=b.cjsModule.exports:g===r&&b.usingExports?g=n[e]:(n[e]=g,H[e]&&(T[e]=!0))}else e&&(g=n[e]=c,H[e]&&(T[e]=!0));if(x[b.id])delete x[b.id],b.isDone=!0,i.waitCount-=1,i.waitCount===0&&(J=[]);delete M[e];if(d.onResourceLoad&&!b.placeholder)d.onResourceLoad(i,h,b.depArray);if(f)return g=(e?l(e).url:"")||f.fileName||f.sourceURL,a=f.moduleTree,f=P("defineerror",'Error evaluating module "'+e+'" at location "'+
|
||||
g+'":\n'+f+"\nfileName:"+g+"\nlineNumber: "+(f.lineNumber||f.line),f),f.moduleName=e,f.moduleTree=a,d.onError(f);for(f=0;c=a[f];f++)c(g);return r}function u(b,f){return function(g){b.depDone[f]||(b.depDone[f]=!0,b.deps[f]=g,b.depCount-=1,b.depCount||p(b))}}function o(b,f){var g=f.map,a=g.fullName,c=g.name,h=N[b]||(N[b]=n[b]),e;if(!f.loading)f.loading=!0,e=function(b){f.callback=function(){return b};p(f);s[f.id]=!0;A()},e.fromText=function(b,f){var g=Q;s[b]=!1;i.scriptCount+=1;i.fake[b]=!0;g&&(Q=!1);
|
||||
d.exec(f);g&&(Q=!0);i.completeLoad(b)},a in n?e(n[a]):h.load(c,t(g.parentMap,!0,function(b,a){var c=[],e,m;for(e=0;m=b[e];e++)m=l(m,g.parentMap),b[e]=m.fullName,m.prefix||c.push(b[e]);f.moduleDeps=(f.moduleDeps||[]).concat(c);return i.require(b,a)}),e,q)}function y(b){x[b.id]||(x[b.id]=b,J.push(b),i.waitCount+=1)}function D(b){this.listeners.push(b)}function v(b,f){var g=b.fullName,a=b.prefix,c=a?N[a]||(N[a]=n[a]):null,h,e;g&&(h=M[g]);if(!h&&(e=!0,h={id:(a&&!c?O++ +"__p@:":"")+(g||"__r@"+O++),map:b,
|
||||
depCount:0,depDone:[],depCallbacks:[],deps:[],listeners:[],add:D},B[h.id]=!0,g&&(!a||N[a])))M[g]=h;a&&!c?(g=l(a),a in n&&!n[a]&&(delete n[a],delete R[g.url]),a=v(g,!0),a.add(function(){var f=l(b.originalName,b.parentMap),f=v(f,!0);h.placeholder=!0;f.add(function(b){h.callback=function(){return b};p(h)})})):e&&f&&(s[h.id]=!1,i.paused.push(h),y(h));return h}function C(b,f,a,c){var b=l(b,c),d=b.name,h=b.fullName,e=v(b),j=e.id,k=e.deps,o;if(h){if(h in n||s[j]===!0||h==="jquery"&&q.jQuery&&q.jQuery!==
|
||||
a().fn.jquery)return;B[j]=!0;s[j]=!0;h==="jquery"&&a&&W(a())}e.depArray=f;e.callback=a;for(a=0;a<f.length;a++)if(j=f[a])j=l(j,d?b:c),o=j.fullName,f[a]=o,o==="require"?k[a]=t(b):o==="exports"?(k[a]=n[h]={},e.usingExports=!0):o==="module"?e.cjsModule=k[a]={id:d,uri:d?i.nameToUrl(d,null,c):r,exports:n[h]}:o in n&&!(o in x)&&(!(h in H)||h in H&&T[o])?k[a]=n[o]:(h in H&&(H[o]=!0,delete n[o],R[j.url]=!1),e.depCount+=1,e.depCallbacks[a]=u(e,a),v(j,!0).add(e.depCallbacks[a]));e.depCount?y(e):p(e)}function w(b){C.apply(null,
|
||||
b)}function F(b,f){var a=b.map.fullName,c=b.depArray,d=!0,h,e,i,l;if(b.isDone||!a||!s[a])return l;if(f[a])return b;f[a]=!0;if(c){for(h=0;h<c.length;h++){e=c[h];if(!s[e]&&!ia[e]){d=!1;break}if((i=x[e])&&!i.isDone&&s[e])if(l=F(i,f))break}d||(l=r,delete f[a])}return l}function z(b,a){var g=b.map.fullName,c=b.depArray,d,h,e,i;if(b.isDone||!g||!s[g])return r;if(g){if(a[g])return n[g];a[g]=!0}if(c)for(d=0;d<c.length;d++)if(h=c[d])if((e=l(h).prefix)&&(i=x[e])&&z(i,a),(e=x[h])&&!e.isDone&&s[h])h=z(e,a),b.depCallbacks[d](h);
|
||||
return n[g]}function E(){var b=q.waitSeconds*1E3,b=b&&i.startTime+b<(new Date).getTime(),a="",c=!1,l=!1,k=[],h,e;if(i.pausedCount>0)return r;if(q.priorityWait)if(j())A();else return r;for(h in s)if(!(h in L)&&(c=!0,!s[h]))if(b)a+=h+" ";else if(l=!0,h.indexOf("!")===-1){k=[];break}else(e=M[h]&&M[h].moduleDeps)&&k.push.apply(k,e);if(!c&&!i.waitCount)return r;if(b&&a)return b=P("timeout","Load timeout for modules: "+a),b.requireType="timeout",b.requireModules=a,b.contextName=i.contextName,d.onError(b);
|
||||
if(l&&k.length)for(a=0;h=x[k[a]];a++)if(h=F(h,{})){z(h,{});break}if(!b&&(l||i.scriptCount)){if((I||da)&&!X)X=setTimeout(function(){X=0;E()},50);return r}if(i.waitCount){for(a=0;h=J[a];a++)z(h,{});i.paused.length&&A();Y<5&&(Y+=1,E())}Y=0;d.checkReadyState();return r}var i,A,q={waitSeconds:7,baseUrl:"./",paths:{},pkgs:{},catchError:{}},S=[],B={require:!0,exports:!0,module:!0},G={},n={},s={},x={},J=[],R={},O=0,M={},N={},H={},T={},Z=0;W=function(b){if(!i.jQuery&&(b=b||(typeof jQuery!=="undefined"?jQuery:
|
||||
null))&&!(q.jQuery&&b.fn.jquery!==q.jQuery)&&("holdReady"in b||"readyWait"in b))if(i.jQuery=b,w(["jquery",[],function(){return jQuery}]),i.scriptCount)V(b,!0),i.jQueryIncremented=!0};A=function(){var b,a,c,l,k,h;i.takeGlobalQueue();Z+=1;if(i.scriptCount<=0)i.scriptCount=0;for(;S.length;)if(b=S.shift(),b[0]===null)return d.onError(P("mismatch","Mismatched anonymous define() module: "+b[b.length-1]));else w(b);if(!q.priorityWait||j())for(;i.paused.length;){k=i.paused;i.pausedCount+=k.length;i.paused=
|
||||
[];for(l=0;b=k[l];l++)a=b.map,c=a.url,h=a.fullName,a.prefix?o(a.prefix,b):!R[c]&&!s[h]&&((q.requireLoad||d.load)(i,h,c),c.indexOf("empty:")!==0&&(R[c]=!0));i.startTime=(new Date).getTime();i.pausedCount-=k.length}Z===1&&E();Z-=1;return r};i={contextName:a,config:q,defQueue:S,waiting:x,waitCount:0,specified:B,loaded:s,urlMap:G,urlFetched:R,scriptCount:0,defined:n,paused:[],pausedCount:0,plugins:N,needFullExec:H,fake:{},fullExec:T,managerCallbacks:M,makeModuleMap:l,normalize:c,configure:function(b){var a,
|
||||
c,d;b.baseUrl&&b.baseUrl.charAt(b.baseUrl.length-1)!=="/"&&(b.baseUrl+="/");a=q.paths;d=q.pkgs;$(q,b,!0);if(b.paths){for(c in b.paths)c in L||(a[c]=b.paths[c]);q.paths=a}if((a=b.packagePaths)||b.packages){if(a)for(c in a)c in L||aa(d,a[c],c);b.packages&&aa(d,b.packages);q.pkgs=d}if(b.priority)c=i.requireWait,i.requireWait=!1,A(),i.require(b.priority),A(),i.requireWait=c,q.priorityWait=b.priority;if(b.deps||b.callback)i.require(b.deps||[],b.callback)},requireDefined:function(b,a){return l(b,a).fullName in
|
||||
n},requireSpecified:function(b,a){return l(b,a).fullName in B},require:function(b,c,g){if(typeof b==="string"){if(K(c))return d.onError(P("requireargs","Invalid require call"));if(d.get)return d.get(i,b,c);c=l(b,c);b=c.fullName;return!(b in n)?d.onError(P("notloaded","Module name '"+c.fullName+"' has not been loaded yet for context: "+a)):n[b]}(b&&b.length||c)&&C(null,b,c,g);if(!i.requireWait)for(;!i.scriptCount&&i.paused.length;)A();return i.require},takeGlobalQueue:function(){U.length&&(ja.apply(i.defQueue,
|
||||
[i.defQueue.length-1,0].concat(U)),U=[])},completeLoad:function(b){var a;for(i.takeGlobalQueue();S.length;)if(a=S.shift(),a[0]===null){a[0]=b;break}else if(a[0]===b)break;else w(a),a=null;a?w(a):w([b,[],b==="jquery"&&typeof jQuery!=="undefined"?function(){return jQuery}:null]);d.isAsync&&(i.scriptCount-=1);A();d.isAsync||(i.scriptCount-=1)},toUrl:function(b,a){var c=b.lastIndexOf("."),d=null;c!==-1&&(d=b.substring(c,b.length),b=b.substring(0,c));return i.nameToUrl(b,d,a)},nameToUrl:function(b,a,g){var l,
|
||||
k,h,e,j=i.config,b=c(b,g&&g.fullName);if(d.jsExtRegExp.test(b))a=b+(a?a:"");else{l=j.paths;k=j.pkgs;g=b.split("/");for(e=g.length;e>0;e--)if(h=g.slice(0,e).join("/"),l[h]){g.splice(0,e,l[h]);break}else if(h=k[h]){b=b===h.name?h.location+"/"+h.main:h.location;g.splice(0,e,b);break}a=g.join("/")+(a||".js");a=(a.charAt(0)==="/"||a.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+a}return j.urlArgs?a+((a.indexOf("?")===-1?"?":"&")+j.urlArgs):a}};i.jQueryCheck=W;i.resume=A;return i}function ka(){var a,c,d;if(C&&C.readyState===
|
||||
"interactive")return C;a=document.getElementsByTagName("script");for(c=a.length-1;c>-1&&(d=a[c]);c--)if(d.readyState==="interactive")return C=d;return null}var la=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,ma=/require\(\s*["']([^'"\s]+)["']\s*\)/g,fa=/^\.\//,ba=/\.js$/,O=Object.prototype.toString,u=Array.prototype,ha=u.slice,ja=u.splice,I=!!(typeof window!=="undefined"&&navigator&&document),da=!I&&typeof importScripts!=="undefined",na=I&&navigator.platform==="PLAYSTATION 3"?/^complete$/:/^(complete|loaded)$/,
|
||||
ea=typeof opera!=="undefined"&&opera.toString()==="[object Opera]",L={},D={},U=[],C=null,Y=0,Q=!1,ia={require:!0,module:!0,exports:!0},d,u={},J,y,v,E,o,w,F,B,z,W,X;if(typeof define==="undefined"){if(typeof requirejs!=="undefined")if(K(requirejs))return;else u=requirejs,requirejs=r;typeof require!=="undefined"&&!K(require)&&(u=require,require=r);d=requirejs=function(a,c,d){var j="_",k;!G(a)&&typeof a!=="string"&&(k=a,G(c)?(a=c,c=d):a=[]);if(k&&k.context)j=k.context;d=D[j]||(D[j]=ga(j));k&&d.configure(k);
|
||||
return d.require(a,c)};d.config=function(a){return d(a)};require||(require=d);d.toUrl=function(a){return D._.toUrl(a)};d.version="1.0.8";d.jsExtRegExp=/^\/|:|\?|\.js$/;y=d.s={contexts:D,skipAsync:{}};if(d.isAsync=d.isBrowser=I)if(v=y.head=document.getElementsByTagName("head")[0],E=document.getElementsByTagName("base")[0])v=y.head=E.parentNode;d.onError=function(a){throw a;};d.load=function(a,c,l){d.resourcesReady(!1);a.scriptCount+=1;d.attach(l,a,c);if(a.jQuery&&!a.jQueryIncremented)V(a.jQuery,!0),
|
||||
a.jQueryIncremented=!0};define=function(a,c,d){var j,k;typeof a!=="string"&&(d=c,c=a,a=null);G(c)||(d=c,c=[]);!c.length&&K(d)&&d.length&&(d.toString().replace(la,"").replace(ma,function(a,d){c.push(d)}),c=(d.length===1?["require"]:["require","exports","module"]).concat(c));if(Q&&(j=J||ka()))a||(a=j.getAttribute("data-requiremodule")),k=D[j.getAttribute("data-requirecontext")];(k?k.defQueue:U).push([a,c,d]);return r};define.amd={multiversion:!0,plugins:!0,jQuery:!0};d.exec=function(a){return eval(a)};
|
||||
d.execCb=function(a,c,d,j){return c.apply(j,d)};d.addScriptToDom=function(a){J=a;E?v.insertBefore(a,E):v.appendChild(a);J=null};d.onScriptLoad=function(a){var c=a.currentTarget||a.srcElement,l;if(a.type==="load"||c&&na.test(c.readyState))C=null,a=c.getAttribute("data-requirecontext"),l=c.getAttribute("data-requiremodule"),D[a].completeLoad(l),c.detachEvent&&!ea?c.detachEvent("onreadystatechange",d.onScriptLoad):c.removeEventListener("load",d.onScriptLoad,!1)};d.attach=function(a,c,l,j,k,o){var p;
|
||||
if(I)return j=j||d.onScriptLoad,p=c&&c.config&&c.config.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script"),p.type=k||c&&c.config.scriptType||"text/javascript",p.charset="utf-8",p.async=!y.skipAsync[a],c&&p.setAttribute("data-requirecontext",c.contextName),p.setAttribute("data-requiremodule",l),p.attachEvent&&!(p.attachEvent.toString&&p.attachEvent.toString().indexOf("[native code]")<0)&&!ea?(Q=!0,o?p.onreadystatechange=function(){if(p.readyState===
|
||||
"loaded")p.onreadystatechange=null,p.attachEvent("onreadystatechange",j),o(p)}:p.attachEvent("onreadystatechange",j)):p.addEventListener("load",j,!1),p.src=a,o||d.addScriptToDom(p),p;else da&&(importScripts(a),c.completeLoad(l));return null};if(I){o=document.getElementsByTagName("script");for(B=o.length-1;B>-1&&(w=o[B]);B--){if(!v)v=w.parentNode;if(F=w.getAttribute("data-main")){if(!u.baseUrl)o=F.split("/"),w=o.pop(),o=o.length?o.join("/")+"/":"./",u.baseUrl=o,F=w.replace(ba,"");u.deps=u.deps?u.deps.concat(F):
|
||||
[F];break}}}d.checkReadyState=function(){var a=y.contexts,c;for(c in a)if(!(c in L)&&a[c].waitCount)return;d.resourcesReady(!0)};d.resourcesReady=function(a){var c,l;d.resourcesDone=a;if(d.resourcesDone)for(l in a=y.contexts,a)if(!(l in L)&&(c=a[l],c.jQueryIncremented))V(c.jQuery,!1),c.jQueryIncremented=!1};d.pageLoaded=function(){if(document.readyState!=="complete")document.readyState="complete"};if(I&&document.addEventListener&&!document.readyState)document.readyState="loading",window.addEventListener("load",
|
||||
d.pageLoaded,!1);d(u);if(d.isAsync&&typeof setTimeout!=="undefined")z=y.contexts[u.context||"_"],z.requireWait=!0,setTimeout(function(){z.requireWait=!1;z.scriptCount||z.resume();d.checkReadyState()},0)}})();
|
||||
@@ -0,0 +1,109 @@
|
||||
(function(window) {
|
||||
|
||||
var ORIGIN_ = location.protocol + '//' + location.host;
|
||||
|
||||
function SlideController() {
|
||||
this.popup = null;
|
||||
this.isPopup = window.opener;
|
||||
|
||||
if (this.setupDone()) {
|
||||
window.addEventListener('message', this.onMessage_.bind(this), false);
|
||||
|
||||
// Close popups if we reload the main window.
|
||||
window.addEventListener('beforeunload', function(e) {
|
||||
if (this.popup) {
|
||||
this.popup.close();
|
||||
}
|
||||
}.bind(this), false);
|
||||
}
|
||||
}
|
||||
|
||||
SlideController.PRESENTER_MODE_PARAM = 'presentme';
|
||||
|
||||
SlideController.prototype.setupDone = function() {
|
||||
var params = location.search.substring(1).split('&').map(function(el) {
|
||||
return el.split('=');
|
||||
});
|
||||
|
||||
var presentMe = null;
|
||||
for (var i = 0, param; param = params[i]; ++i) {
|
||||
if (param[0].toLowerCase() == SlideController.PRESENTER_MODE_PARAM) {
|
||||
presentMe = param[1] == 'true';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (presentMe !== null) {
|
||||
localStorage.ENABLE_PRESENTOR_MODE = presentMe;
|
||||
// TODO: use window.history.pushState to update URL instead of the redirect.
|
||||
if (window.history.replaceState) {
|
||||
window.history.replaceState({}, '', location.pathname);
|
||||
} else {
|
||||
location.replace(location.pathname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var enablePresenterMode = localStorage.getItem('ENABLE_PRESENTOR_MODE');
|
||||
if (enablePresenterMode && JSON.parse(enablePresenterMode)) {
|
||||
// Only open popup from main deck. Don't want recursive popup opening!
|
||||
if (!this.isPopup) {
|
||||
var opts = 'menubar=no,location=yes,resizable=yes,scrollbars=no,status=no';
|
||||
this.popup = window.open(location.href, 'mywindow', opts);
|
||||
|
||||
// Loading in the popup? Trigger the hotkey for turning presenter mode on.
|
||||
this.popup.addEventListener('load', function(e) {
|
||||
var evt = this.popup.document.createEvent('Event');
|
||||
evt.initEvent('keydown', true, true);
|
||||
evt.keyCode = 'P'.charCodeAt(0);
|
||||
this.popup.document.dispatchEvent(evt);
|
||||
// this.popup.document.body.classList.add('with-notes');
|
||||
// document.body.classList.add('popup');
|
||||
}.bind(this), false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SlideController.prototype.onMessage_ = function(e) {
|
||||
var data = e.data;
|
||||
|
||||
// Restrict messages to being from this origin. Allow local developmet
|
||||
// from file:// though.
|
||||
// TODO: It would be dope if FF implemented location.origin!
|
||||
if (e.origin != ORIGIN_ && ORIGIN_.indexOf('file://') != 0) {
|
||||
alert('Someone tried to postMessage from an unknown origin');
|
||||
return;
|
||||
}
|
||||
|
||||
// if (e.source.location.hostname != 'localhost') {
|
||||
// alert('Someone tried to postMessage from an unknown origin');
|
||||
// return;
|
||||
// }
|
||||
|
||||
if ('keyCode' in data) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('keydown', true, true);
|
||||
evt.keyCode = data.keyCode;
|
||||
document.dispatchEvent(evt);
|
||||
}
|
||||
};
|
||||
|
||||
SlideController.prototype.sendMsg = function(msg) {
|
||||
// // Send message to popup window.
|
||||
// if (this.popup) {
|
||||
// this.popup.postMessage(msg, ORIGIN_);
|
||||
// }
|
||||
|
||||
// Send message to main window.
|
||||
if (this.isPopup) {
|
||||
// TODO: It would be dope if FF implemented location.origin.
|
||||
window.opener.postMessage(msg, '*');
|
||||
}
|
||||
};
|
||||
|
||||
window.SlideController = SlideController;
|
||||
|
||||
})(window);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
// Polyfill missing APIs (if we need to), then create the slide deck.
|
||||
// iOS < 5 needs classList, dataset, and window.matchMedia. Modernizr contains
|
||||
// the last one.
|
||||
(function() {
|
||||
Modernizr.load({
|
||||
test: !!document.body.classList && !!document.body.dataset,
|
||||
nope: ['js/polyfills/classList.min.js', 'js/polyfills/dataset.min.js'],
|
||||
complete: function() {
|
||||
window.slidedeck = new SlideDeck();
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,897 @@
|
||||
/**
|
||||
* @authors Luke Mahe
|
||||
* @authors Eric Bidelman
|
||||
* @fileoverview TODO
|
||||
*/
|
||||
document.cancelFullScreen = document.webkitCancelFullScreen ||
|
||||
document.mozCancelFullScreen;
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function SlideDeck(el) {
|
||||
this.curSlide_ = 0;
|
||||
this.prevSlide_ = 0;
|
||||
this.config_ = null;
|
||||
this.container = el || document.querySelector('slides');
|
||||
this.slides = [];
|
||||
this.controller = null;
|
||||
|
||||
this.getCurrentSlideFromHash_();
|
||||
|
||||
// Call this explicitly. Modernizr.load won't be done until after DOM load.
|
||||
this.onDomLoaded_.bind(this)();
|
||||
}
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.SLIDE_CLASSES_ = [
|
||||
'far-past', 'past', 'current', 'next', 'far-next'];
|
||||
|
||||
/**
|
||||
* @const
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.CSS_DIR_ = '_static/theme/css/';
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.findSlideById = function(title_id) {
|
||||
// Return the 1-base index of the Slide with id ``title_id``
|
||||
//
|
||||
// The index must be 1-based, as it's passed to code which assumes
|
||||
// it was specified as the location fragment.
|
||||
|
||||
slideEls = document.querySelectorAll('slides > slide');
|
||||
|
||||
for (var i = 0; i < slideEls.length; i++) {
|
||||
if (slideEls.item(i).id == title_id) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// no match on a slide, perhaps it's an explicit reference?
|
||||
var
|
||||
target_link = document.querySelector("span[id='" + title_id + "']"),
|
||||
// XXX this is pretty strict, may need to be more flexible in the future
|
||||
slide = (target_link && target_link.parentNode);
|
||||
|
||||
if (slide && slide.tagName == 'SLIDE') {
|
||||
return this.findSlideById(slide.id);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.getCurrentSlideFromHash_ = function() {
|
||||
var slideNo = parseInt(document.location.hash.substr(1));
|
||||
|
||||
if (slideNo && isNaN(slideNo)) {
|
||||
// must be a section title reference
|
||||
slideNo = this.findSlideById(location.hash.substr(1));
|
||||
}
|
||||
|
||||
if (slideNo) {
|
||||
this.curSlide_ = slideNo - 1;
|
||||
} else {
|
||||
this.curSlide_ = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.loadSlide = function(slideNo) {
|
||||
if (slideNo) {
|
||||
this.curSlide_ = slideNo - 1;
|
||||
this.updateSlides_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.onDomLoaded_ = function(e) {
|
||||
document.body.classList.add('loaded'); // Add loaded class for templates to use.
|
||||
|
||||
this.slides = this.container.querySelectorAll('slide:not([hidden]):not(.hidden):not(.backdrop)');
|
||||
|
||||
// If we're on a smartphone, apply special sauce.
|
||||
if (Modernizr.mq('only screen and (max-device-width: 480px)')) {
|
||||
// var style = document.createElement('link');
|
||||
// style.rel = 'stylesheet';
|
||||
// style.type = 'text/css';
|
||||
// style.href = this.CSS_DIR_ + 'phone.css';
|
||||
// document.querySelector('head').appendChild(style);
|
||||
|
||||
// No need for widescreen layout on a phone.
|
||||
this.container.classList.remove('layout-widescreen');
|
||||
}
|
||||
|
||||
this.loadConfig_(SLIDE_CONFIG);
|
||||
this.addEventListeners_();
|
||||
this.updateSlides_();
|
||||
|
||||
// Add slide numbers and total slide count metadata to each slide.
|
||||
var that = this;
|
||||
for (var i = 0, slide; slide = this.slides[i]; ++i) {
|
||||
slide.dataset.slideNum = i + 1;
|
||||
slide.dataset.totalSlides = this.slides.length;
|
||||
|
||||
slide.addEventListener('click', function(e) {
|
||||
if (document.body.classList.contains('overview')) {
|
||||
that.loadSlide(this.dataset.slideNum);
|
||||
e.preventDefault();
|
||||
window.setTimeout(function() {
|
||||
that.toggleOverview();
|
||||
}, 500);
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
// Note: this needs to come after addEventListeners_(), which adds a
|
||||
// 'keydown' listener that this controller relies on.
|
||||
|
||||
// Modernizr.touch isn't a sufficient check for devices that support both
|
||||
// touch and mouse. Create the controller in all cases.
|
||||
// // Also, no need to set this up if we're on mobile.
|
||||
// if (!Modernizr.touch) {
|
||||
this.controller = new SlideController(this);
|
||||
if (this.controller.isPopup) {
|
||||
document.body.classList.add('popup');
|
||||
}
|
||||
//}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.addEventListeners_ = function() {
|
||||
document.addEventListener('keydown', this.onBodyKeyDown_.bind(this), false);
|
||||
window.addEventListener('popstate', this.onPopState_.bind(this), false);
|
||||
|
||||
// var transEndEventNames = {
|
||||
// 'WebkitTransition': 'webkitTransitionEnd',
|
||||
// 'MozTransition': 'transitionend',
|
||||
// 'OTransition': 'oTransitionEnd',
|
||||
// 'msTransition': 'MSTransitionEnd',
|
||||
// 'transition': 'transitionend'
|
||||
// };
|
||||
//
|
||||
// // Find the correct transitionEnd vendor prefix.
|
||||
// window.transEndEventName = transEndEventNames[
|
||||
// Modernizr.prefixed('transition')];
|
||||
//
|
||||
// // When slides are done transitioning, kickoff loading iframes.
|
||||
// // Note: we're only looking at a single transition (on the slide). This
|
||||
// // doesn't include autobuilds the slides may have. Also, if the slide
|
||||
// // transitions on multiple properties (e.g. not just 'all'), this doesn't
|
||||
// // handle that case.
|
||||
// this.container.addEventListener(transEndEventName, function(e) {
|
||||
// this.enableSlideFrames_(this.curSlide_);
|
||||
// }.bind(this), false);
|
||||
|
||||
// document.addEventListener('slideenter', function(e) {
|
||||
// var slide = e.target;
|
||||
// window.setTimeout(function() {
|
||||
// this.enableSlideFrames_(e.slideNumber);
|
||||
// this.enableSlideFrames_(e.slideNumber + 1);
|
||||
// }.bind(this), 300);
|
||||
// }.bind(this), false);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} e The pop event.
|
||||
*/
|
||||
SlideDeck.prototype.onPopState_ = function(e) {
|
||||
if (e.state != null) {
|
||||
this.curSlide_ = e.state;
|
||||
this.updateSlides_(true);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Event} e
|
||||
*/
|
||||
SlideDeck.prototype.onBodyKeyDown_ = function(e) {
|
||||
if (/^(input|textarea)$/i.test(e.target.nodeName) ||
|
||||
e.target.isContentEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Forward keydowns to the main slides if we're the popup.
|
||||
if (this.controller && this.controller.isPopup) {
|
||||
this.controller.sendMsg({keyCode: e.keyCode});
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 13: // Enter
|
||||
if (document.body.classList.contains('overview')) {
|
||||
this.toggleOverview();
|
||||
}
|
||||
break;
|
||||
|
||||
case 39: // right arrow
|
||||
case 32: // space
|
||||
case 34: // PgDn
|
||||
this.nextSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 37: // left arrow
|
||||
case 8: // Backspace
|
||||
case 33: // PgUp
|
||||
this.prevSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 40: // down arrow
|
||||
this.nextSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 38: // up arrow
|
||||
this.prevSlide();
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 72: // H: Toggle code highlighting
|
||||
document.body.classList.toggle('highlight-code');
|
||||
break;
|
||||
|
||||
case 79: // O: Toggle overview
|
||||
this.toggleOverview();
|
||||
break;
|
||||
|
||||
case 80: // P
|
||||
if (this.controller && this.controller.isPopup) {
|
||||
document.body.classList.toggle('with-notes');
|
||||
} else if (this.controller && !this.controller.popup) {
|
||||
document.body.classList.toggle('with-notes');
|
||||
}
|
||||
break;
|
||||
|
||||
case 82: // R
|
||||
// TODO: implement refresh on main slides when popup is refreshed.
|
||||
break;
|
||||
|
||||
case 27: // ESC: Hide notes and highlighting
|
||||
document.body.classList.remove('with-notes');
|
||||
document.body.classList.remove('highlight-code');
|
||||
|
||||
if (document.body.classList.contains('overview')) {
|
||||
this.toggleOverview();
|
||||
}
|
||||
break;
|
||||
|
||||
case 70: // F: Toggle fullscreen
|
||||
// Only respect 'f' on body. Don't want to capture keys from an <input>.
|
||||
// Also, ignore browser's fullscreen shortcut (cmd+shift+f) so we don't
|
||||
// get trapped in fullscreen!
|
||||
if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
|
||||
if (document.mozFullScreen !== undefined && !document.mozFullScreen) {
|
||||
document.body.mozRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else if (document.webkitIsFullScreen !== undefined && !document.webkitIsFullScreen) {
|
||||
document.body.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
} else {
|
||||
document.cancelFullScreen();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 87: // W: Toggle widescreen
|
||||
// Only respect 'w' on body. Don't want to capture keys from an <input>.
|
||||
if (e.target == document.body && !(e.shiftKey && e.metaKey)) {
|
||||
this.container.classList.toggle('layout-widescreen');
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SlideDeck.prototype.focusOverview_ = function() {
|
||||
var overview = document.body.classList.contains('overview');
|
||||
|
||||
for (var i = 0, slide; slide = this.slides[i]; i++) {
|
||||
slide.style[Modernizr.prefixed('transform')] = overview ?
|
||||
'translateZ(-2500px) translate(' + (( i - this.curSlide_ ) * 105) +
|
||||
'%, 0%)' : '';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
SlideDeck.prototype.toggleOverview = function() {
|
||||
document.body.classList.toggle('overview');
|
||||
|
||||
this.focusOverview_();
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.loadConfig_ = function(config) {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.config_ = config;
|
||||
|
||||
var settings = this.config_.settings;
|
||||
|
||||
this.loadTheme_(settings.theme || []);
|
||||
|
||||
if (settings.favIcon) {
|
||||
this.addFavIcon_(settings.favIcon);
|
||||
}
|
||||
|
||||
// Prettyprint. Default to on.
|
||||
if (!!!('usePrettify' in settings) || settings.usePrettify) {
|
||||
prettyPrint();
|
||||
}
|
||||
|
||||
if (settings.analytics) {
|
||||
this.loadAnalytics_();
|
||||
}
|
||||
|
||||
if (settings.fonts) {
|
||||
this.addFonts_(settings.fonts);
|
||||
}
|
||||
|
||||
// Builds. Default to on.
|
||||
if (!!!('useBuilds' in settings) || settings.useBuilds) {
|
||||
this.makeBuildLists_();
|
||||
}
|
||||
|
||||
if (settings.title) {
|
||||
document.title = settings.title.replace(/<br\/?>/, ' ');
|
||||
if (settings.eventInfo && settings.eventInfo.title) {
|
||||
document.title += ' - ' + settings.eventInfo.title;
|
||||
}
|
||||
document.querySelector('[data-config-title]').innerHTML = settings.title;
|
||||
}
|
||||
|
||||
if (settings.subtitle) {
|
||||
document.querySelector('[data-config-subtitle]').innerHTML = settings.subtitle;
|
||||
}
|
||||
|
||||
if (this.config_.presenters) {
|
||||
var presenters = this.config_.presenters;
|
||||
var dataConfigContact = document.querySelector('[data-config-contact]');
|
||||
|
||||
var html = [];
|
||||
if (presenters.length == 1) {
|
||||
var p = presenters[0];
|
||||
|
||||
var presenterTitle = [p.name];
|
||||
if (p.company) {
|
||||
presenterTitle.push(p.company);
|
||||
}
|
||||
html = presenterTitle.join(' - ') + '<br>';
|
||||
|
||||
var gplus = p.gplus ? '<span>g+</span><a href="' + p.gplus +
|
||||
'">' + p.gplus.replace(/https?:\/\//, '') + '</a>' : '';
|
||||
|
||||
var twitter = p.twitter ? '<span>twitter</span>' +
|
||||
'<a href="http://twitter.com/' + p.twitter + '">' +
|
||||
p.twitter + '</a>' : '';
|
||||
|
||||
var www = p.www ? '<span>www</span><a href="' + p.www +
|
||||
'">' + p.www.replace(/https?:\/\//, '') + '</a>' : '';
|
||||
|
||||
var github = p.github ? '<span>github</span><a href="' + p.github +
|
||||
'">' + p.github.replace(/https?:\/\//, '') + '</a>' : '';
|
||||
|
||||
var html2 = [gplus, twitter, www, github].join('<br>');
|
||||
|
||||
if (dataConfigContact) {
|
||||
dataConfigContact.innerHTML = html2;
|
||||
}
|
||||
} else {
|
||||
for (var i = 0, p; p = presenters[i]; ++i) {
|
||||
html.push(p.name + ' - ' + p.company);
|
||||
}
|
||||
html = html.join('<br>');
|
||||
if (dataConfigContact) {
|
||||
dataConfigContact.innerHTML = html;
|
||||
}
|
||||
}
|
||||
|
||||
var dataConfigPresenter = document.querySelector('[data-config-presenter]');
|
||||
if (dataConfigPresenter) {
|
||||
dataConfigPresenter.innerHTML = html;
|
||||
if (settings.eventInfo) {
|
||||
var date = settings.eventInfo.date;
|
||||
var dateInfo = date ? ' - <time>' + date + '</time>' : '';
|
||||
dataConfigPresenter.innerHTML += settings.eventInfo.title + dateInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Left/Right tap areas. Default to including. */
|
||||
if (!!!('enableSlideAreas' in settings) || settings.enableSlideAreas) {
|
||||
var el = document.createElement('div');
|
||||
el.classList.add('slide-area');
|
||||
el.id = 'prev-slide-area';
|
||||
el.addEventListener('click', this.prevSlide.bind(this,undefined), false);
|
||||
this.container.appendChild(el);
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.classList.add('slide-area');
|
||||
el.id = 'next-slide-area';
|
||||
el.addEventListener('click', this.nextSlide.bind(this,undefined), false);
|
||||
this.container.appendChild(el);
|
||||
}
|
||||
|
||||
if (Modernizr.touch && (!!!('enableTouch' in settings) ||
|
||||
settings.enableTouch)) {
|
||||
var self = this;
|
||||
|
||||
// Note: this prevents mobile zoom in/out but prevents iOS from doing
|
||||
// it's crazy scroll over effect and disaligning the slides.
|
||||
window.addEventListener('touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
var hammer = new Hammer(this.container);
|
||||
hammer.ondragend = function(e) {
|
||||
if (e.direction == 'right' || e.direction == 'down') {
|
||||
self.prevSlide();
|
||||
} else if (e.direction == 'left' || e.direction == 'up') {
|
||||
self.nextSlide();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Array.<string>} fonts
|
||||
*/
|
||||
SlideDeck.prototype.addFonts_ = function(fonts) {
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'stylesheet';
|
||||
el.href = ('https:' == document.location.protocol ? 'https' : 'http') +
|
||||
'://fonts.googleapis.com/css?family=' + fonts.join('|') + '&v2';
|
||||
document.querySelector('head').appendChild(el);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.buildNextBuildItem_ = function() {
|
||||
var slide = this.slides[this.curSlide_];
|
||||
var toBuild = slide.querySelector('.to-build');
|
||||
var built = slide.querySelector('.build-current');
|
||||
|
||||
if (built) {
|
||||
built.classList.remove('build-current');
|
||||
if (built.classList.contains('fade')) {
|
||||
built.classList.add('build-fade');
|
||||
}
|
||||
}
|
||||
|
||||
if (!toBuild) {
|
||||
var items = slide.querySelectorAll('.build-fade');
|
||||
for (var j = 0, item; item = items[j]; j++) {
|
||||
item.classList.remove('build-fade');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
toBuild.classList.remove('to-build');
|
||||
toBuild.classList.add('build-current');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
SlideDeck.prototype.buildNextItem_ = function() {
|
||||
|
||||
var slide = this.slides[this.curSlide_];
|
||||
var built = slide.querySelectorAll('.build-current');
|
||||
|
||||
var buildItems = slide.querySelectorAll('[class*="build-item-"]');
|
||||
var show_items;
|
||||
|
||||
// Remove the classes from the previously built item
|
||||
if (built) {
|
||||
for (var j = 0, built_item; built_item = built[j]; ++j) {
|
||||
built_item.classList.remove('build-current');
|
||||
if (built_item.classList.contains('fade')) {
|
||||
built_item.classList.add('build-fade');
|
||||
}
|
||||
|
||||
if (built_item.getAttribute('data-build-show-only')) {
|
||||
|
||||
if (built_item.getAttribute('data-build-class')) {
|
||||
built_item.classList.remove(
|
||||
built_item.getAttribute('data-build-class')
|
||||
);
|
||||
} else {
|
||||
built_item.classList.add('build-hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (slide._buildItems && slide._buildItems.length) {
|
||||
while ((show_items = slide._buildItems.shift()) === undefined) {};
|
||||
if (show_items) {
|
||||
|
||||
// show the next items
|
||||
show_items.forEach(function(item, index, items) {
|
||||
item.classList.remove('to-build');
|
||||
item.classList.add('build-current');
|
||||
|
||||
if (item.getAttribute('data-build-class')) {
|
||||
item.classList.add(item.getAttribute('data-build-class'));
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.buildNextBuildItem_();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean=} opt_dontPush
|
||||
*/
|
||||
SlideDeck.prototype.prevSlide = function(opt_dontPush) {
|
||||
if (this.curSlide_ > 0) {
|
||||
var bodyClassList = document.body.classList;
|
||||
bodyClassList.remove('highlight-code');
|
||||
|
||||
// Toggle off speaker notes if they're showing when we move backwards on the
|
||||
// main slides. If we're the speaker notes popup, leave them up.
|
||||
if (this.controller && !this.controller.isPopup) {
|
||||
bodyClassList.remove('with-notes');
|
||||
} else if (!this.controller) {
|
||||
bodyClassList.remove('with-notes');
|
||||
}
|
||||
|
||||
this.prevSlide_ = this.curSlide_--;
|
||||
|
||||
this.updateSlides_(opt_dontPush);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean=} opt_dontPush
|
||||
*/
|
||||
SlideDeck.prototype.nextSlide = function(opt_dontPush) {
|
||||
if (!document.body.classList.contains('overview') && this.buildNextItem_()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.curSlide_ < this.slides.length - 1) {
|
||||
var bodyClassList = document.body.classList;
|
||||
bodyClassList.remove('highlight-code');
|
||||
|
||||
// Toggle off speaker notes if they're showing when we advanced on the main
|
||||
// slides. If we're the speaker notes popup, leave them up.
|
||||
if (this.controller && !this.controller.isPopup) {
|
||||
bodyClassList.remove('with-notes');
|
||||
} else if (!this.controller) {
|
||||
bodyClassList.remove('with-notes');
|
||||
}
|
||||
|
||||
this.prevSlide_ = this.curSlide_++;
|
||||
|
||||
this.updateSlides_(opt_dontPush);
|
||||
}
|
||||
};
|
||||
|
||||
/* Slide events */
|
||||
|
||||
/**
|
||||
* Triggered when a slide enter/leave event should be dispatched.
|
||||
*
|
||||
* @param {string} type The type of event to trigger
|
||||
* (e.g. 'slideenter', 'slideleave').
|
||||
* @param {number} slideNo The index of the slide that is being left.
|
||||
*/
|
||||
SlideDeck.prototype.triggerSlideEvent = function(type, slideNo) {
|
||||
var el = this.getSlideEl_(slideNo);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call onslideenter/onslideleave if the attribute is defined on this slide.
|
||||
var func = el.getAttribute(type);
|
||||
if (func) {
|
||||
new Function(func).call(el); // TODO: Don't use new Function() :(
|
||||
}
|
||||
|
||||
// Dispatch event to listeners setup using addEventListener.
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent(type, true, true);
|
||||
evt.slideNumber = slideNo + 1; // Make it readable
|
||||
evt.slide = el;
|
||||
|
||||
el.dispatchEvent(evt);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.updateSlides_ = function(opt_dontPush) {
|
||||
var dontPush = opt_dontPush || false;
|
||||
|
||||
var curSlide = this.curSlide_;
|
||||
for (var i = 0; i < this.slides.length; ++i) {
|
||||
switch (i) {
|
||||
case curSlide - 2:
|
||||
this.updateSlideClass_(i, 'far-past');
|
||||
break;
|
||||
case curSlide - 1:
|
||||
this.updateSlideClass_(i, 'past');
|
||||
break;
|
||||
case curSlide:
|
||||
this.updateSlideClass_(i, 'current');
|
||||
break;
|
||||
case curSlide + 1:
|
||||
this.updateSlideClass_(i, 'next');
|
||||
break;
|
||||
case curSlide + 2:
|
||||
this.updateSlideClass_(i, 'far-next');
|
||||
break;
|
||||
default:
|
||||
this.updateSlideClass_(i);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
this.triggerSlideEvent('slideleave', this.prevSlide_);
|
||||
this.triggerSlideEvent('slideenter', curSlide);
|
||||
|
||||
// window.setTimeout(this.disableSlideFrames_.bind(this, curSlide - 2), 301);
|
||||
//
|
||||
// this.enableSlideFrames_(curSlide - 1); // Previous slide.
|
||||
// this.enableSlideFrames_(curSlide + 1); // Current slide.
|
||||
// this.enableSlideFrames_(curSlide + 2); // Next slide.
|
||||
|
||||
// Enable current slide's iframes (needed for page loat at current slide).
|
||||
this.enableSlideFrames_(curSlide + 1);
|
||||
|
||||
// No way to tell when all slide transitions + auto builds are done.
|
||||
// Give ourselves a good buffer to preload the next slide's iframes.
|
||||
window.setTimeout(this.enableSlideFrames_.bind(this, curSlide + 2), 1000);
|
||||
|
||||
this.updateHash_(dontPush);
|
||||
|
||||
if (document.body.classList.contains('overview')) {
|
||||
this.focusOverview_();
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.enableSlideFrames_ = function(slideNo) {
|
||||
var el = this.slides[slideNo - 1];
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frames = el.querySelectorAll('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
this.enableFrame_(frame);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.enableFrame_ = function(frame) {
|
||||
var src = frame.dataset.src;
|
||||
if (src && frame.src != src) {
|
||||
frame.src = src;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.disableSlideFrames_ = function(slideNo) {
|
||||
var el = this.slides[slideNo - 1];
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
var frames = el.querySelectorAll('iframe');
|
||||
for (var i = 0, frame; frame = frames[i]; i++) {
|
||||
this.disableFrame_(frame);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Node} frame
|
||||
*/
|
||||
SlideDeck.prototype.disableFrame_ = function(frame) {
|
||||
frame.src = 'about:blank';
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
*/
|
||||
SlideDeck.prototype.getSlideEl_ = function(no) {
|
||||
if ((no < 0) || (no >= this.slides.length)) {
|
||||
return null;
|
||||
} else {
|
||||
return this.slides[no];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {number} slideNo
|
||||
* @param {string} className
|
||||
*/
|
||||
SlideDeck.prototype.updateSlideClass_ = function(slideNo, className) {
|
||||
var el = this.getSlideEl_(slideNo);
|
||||
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (className) {
|
||||
el.classList.add(className);
|
||||
}
|
||||
|
||||
for (var i = 0, slideClass; slideClass = this.SLIDE_CLASSES_[i]; ++i) {
|
||||
if (className != slideClass) {
|
||||
el.classList.remove(slideClass);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.BUILD_ITEM_RE = /build-item-(\d+)(-class-(\w+))?(-only)?/;
|
||||
|
||||
SlideDeck.prototype.makeBuildLists_ = function () {
|
||||
for (var i = this.curSlide_, slide; slide = this.slides[i]; ++i) {
|
||||
var items = slide.querySelectorAll('.build > *');
|
||||
|
||||
for (var j = 0, item; item = items[j]; ++j) {
|
||||
if (item.classList) {
|
||||
item.classList.add('to-build');
|
||||
if (item.parentNode.classList.contains('fade')) {
|
||||
item.classList.add('fade');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var items = slide.querySelectorAll('[class*="build-item-"]');
|
||||
if (items.length) {
|
||||
slide._buildItems = [];
|
||||
};
|
||||
for (var j = 0, item; item = items[j]; ++j) {
|
||||
if (item.classList) {
|
||||
item.classList.add('to-build');
|
||||
if (!item.parentNode.classList.contains('build')) {
|
||||
item.parentNode.classList.add('build');
|
||||
}
|
||||
if (item.parentNode.classList.contains('fade')) {
|
||||
item.classList.add('fade');
|
||||
}
|
||||
}
|
||||
|
||||
var build_info = this.BUILD_ITEM_RE.exec(item.classList),
|
||||
build_index = build_info[1],
|
||||
build_class = build_info[3],
|
||||
build_only = build_info[4];
|
||||
|
||||
if (slide._buildItems[build_index] === undefined) {
|
||||
slide._buildItems[build_index] = [];
|
||||
}
|
||||
slide._buildItems[build_index].push(item);
|
||||
|
||||
if (build_class) {
|
||||
item.setAttribute('data-build-class', build_class);
|
||||
}
|
||||
|
||||
if (build_only) {
|
||||
// add the data-attribute
|
||||
item.setAttribute('data-build-show-only', build_index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {boolean} dontPush
|
||||
*/
|
||||
SlideDeck.prototype.updateHash_ = function(dontPush) {
|
||||
if (!dontPush) {
|
||||
var slideNo = this.curSlide_ + 1;
|
||||
var hash = '#' + slideNo;
|
||||
if (window.history.pushState) {
|
||||
window.history.pushState(this.curSlide_, 'Slide ' + slideNo, hash);
|
||||
} else {
|
||||
window.location.replace(hash);
|
||||
}
|
||||
|
||||
// Record GA hit on this slide.
|
||||
window['_gaq'] && window['_gaq'].push(['_trackPageview',
|
||||
document.location.href]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} favIcon
|
||||
*/
|
||||
SlideDeck.prototype.addFavIcon_ = function(favIcon) {
|
||||
var el = document.createElement('link');
|
||||
el.rel = 'icon';
|
||||
el.type = 'image/png';
|
||||
el.href = favIcon;
|
||||
document.querySelector('head').appendChild(el);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} theme
|
||||
*/
|
||||
SlideDeck.prototype.loadTheme_ = function(theme) {
|
||||
var styles = [];
|
||||
if (theme.constructor.name === 'String') {
|
||||
styles.push(theme);
|
||||
} else {
|
||||
styles = theme;
|
||||
}
|
||||
|
||||
for (var i = 0, style; themeUrl = styles[i]; i++) {
|
||||
var style = document.createElement('link');
|
||||
style.rel = 'stylesheet';
|
||||
style.type = 'text/css';
|
||||
if (themeUrl.indexOf('http') == -1) {
|
||||
style.href = this.CSS_DIR_ + themeUrl + '.css';
|
||||
} else {
|
||||
style.href = themeUrl;
|
||||
}
|
||||
document.querySelector('head').appendChild(style);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
SlideDeck.prototype.loadAnalytics_ = function() {
|
||||
var _gaq = window['_gaq'] || [];
|
||||
_gaq.push(['_setAccount', this.config_.settings.analytics]);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
require(['order!modernizr.custom.45394',
|
||||
'order!prettify/prettify', 'order!hammer', 'order!slide-controller',
|
||||
'order!slide-deck',
|
||||
'order!slide-deck-instantiate'], function(someModule) {
|
||||
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
require(['order!../slide_config', 'order!modernizr.custom.45394',
|
||||
'order!prettify/prettify', 'order!hammer', 'order!slide-controller',
|
||||
'order!slide-deck',
|
||||
'order!slide-deck-instantiate'], function(someModule) {
|
||||
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
var SLIDE_CONFIG = {
|
||||
// Slide settings
|
||||
settings: {
|
||||
title: 'Title Goes Here<br>Up To Two Lines',
|
||||
subtitle: 'Subtitle Goes Here',
|
||||
//eventInfo: {
|
||||
// title: 'Google I/O',
|
||||
// date: '6/x/2013'
|
||||
//},
|
||||
useBuilds: true, // Default: true. False will turn off slide animation builds.
|
||||
usePrettify: true, // Default: true
|
||||
enableSlideAreas: true, // Default: true. False turns off the click areas on either slide of the slides.
|
||||
enableTouch: true, // Default: true. If touch support should enabled. Note: the device must support touch.
|
||||
//analytics: 'UA-XXXXXXXX-1', // TODO: Using this breaks GA for some reason (probably requirejs). Update your tracking code in template.html instead.
|
||||
favIcon: 'images/google_developers_logo_tiny.png',
|
||||
fonts: [
|
||||
'Open Sans:regular,semibold,italic,italicsemibold',
|
||||
'Source Code Pro'
|
||||
],
|
||||
//theme: ['mytheme'], // Add your own custom themes or styles in /theme/css. Leave off the .css extension.
|
||||
},
|
||||
|
||||
// Author information
|
||||
presenters: [{
|
||||
name: 'Firstname Lastname',
|
||||
company: 'Job Title<br>Google',
|
||||
gplus: 'http://plus.google.com/1234567890',
|
||||
twitter: '@yourhandle',
|
||||
www: 'http://www.you.com',
|
||||
github: 'http://github.com/you'
|
||||
}/*, {
|
||||
name: 'Second Name',
|
||||
company: 'Job Title, Google',
|
||||
gplus: 'http://plus.google.com/1234567890',
|
||||
twitter: '@yourhandle',
|
||||
www: 'http://www.you.com',
|
||||
github: 'http://github.com/you'
|
||||
}*/]
|
||||
};
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
var SLIDE_CONFIG = {
|
||||
// Slide settings
|
||||
settings: {
|
||||
title: '{{ docstitle|e }}',
|
||||
subtitle: '{{ theme_subtitle|e }}',
|
||||
//eventInfo: {
|
||||
// title: 'Google I/O',
|
||||
// date: '6/x/2013'
|
||||
//},
|
||||
useBuilds: {{ theme_use_builds }}, // Default: true. False will turn off slide animation builds.
|
||||
usePrettify: {{ theme_use_prettify }}, // Default: true
|
||||
enableSlideAreas: {{ theme_enable_slide_areas }}, // Default: true. False turns off the click areas on either slide of the slides.
|
||||
enableTouch: {{ theme_enable_touch }}, // Default: true. If touch support should enabled. Note: the device must support touch.
|
||||
//analytics: 'UA-XXXXXXXX-1', // TODO: Using this breaks GA for some reason (probably requirejs). Update your tracking code in template.html instead.
|
||||
favIcon: {{ theme_favicon }},
|
||||
fonts: [
|
||||
'Open Sans:regular,semibold,italic,italicsemibold',
|
||||
'Source Code Pro'
|
||||
],
|
||||
//theme: ['mytheme'], // Add your own custom themes or styles in /theme/css. Leave off the .css extension.
|
||||
},
|
||||
|
||||
// Author information
|
||||
presenters: {% if theme_presenters %}{{ theme_presenters|json }}
|
||||
{% else %}[]
|
||||
{% endif %}
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
/* line 5, ../scss/hieroglyph.scss */
|
||||
ol {
|
||||
margin-left: 1.2em;
|
||||
margin-bottom: 1em;
|
||||
position: relative;
|
||||
list-style: decimal;
|
||||
}
|
||||
/* line 11, ../scss/hieroglyph.scss */
|
||||
ol li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
/* line 14, ../scss/hieroglyph.scss */
|
||||
ol li ol {
|
||||
margin-left: 2em;
|
||||
margin-bottom: 0;
|
||||
list-style: decimal;
|
||||
}
|
||||
/* line 19, ../scss/hieroglyph.scss */
|
||||
ol li ol li:before {
|
||||
font-weight: 600;
|
||||
}
|
||||
/* line 25, ../scss/hieroglyph.scss */
|
||||
ol ol {
|
||||
margin-top: .5em;
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
/* line 32, ../scss/hieroglyph.scss */
|
||||
slide.title-image {
|
||||
padding-right: 0px;
|
||||
}
|
||||
/* line 36, ../scss/hieroglyph.scss */
|
||||
slide.title-image hgroup {
|
||||
position: static !important;
|
||||
margin-top: 35%;
|
||||
padding-left: 30px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border-top-left-radius: 5px;
|
||||
-webkit-border-top-left-radius: 5px;
|
||||
-moz-border-top-left-radius: 5px;
|
||||
-o-border-top-left-radius: 5px;
|
||||
}
|
||||
/* line 50, ../scss/hieroglyph.scss */
|
||||
slide.title-image hgroup + article {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
margin-top: 0px;
|
||||
padding-left: 30px;
|
||||
border-bottom-left-radius: 5px;
|
||||
-webkit-border-bottom-left-radius: 5px;
|
||||
-moz-border-bottom-left-radius: 5px;
|
||||
-o-border-bottom-left-radius: 5px;
|
||||
}
|
||||
/* line 62, ../scss/hieroglyph.scss */
|
||||
slide.title-image h1 {
|
||||
color: #222;
|
||||
font-size: 3.2em;
|
||||
line-height: 1.5em;
|
||||
font-weight: 500;
|
||||
}
|
||||
/* line 72, ../scss/hieroglyph.scss */
|
||||
slide.title-image div.figure img {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
z-index: -1;
|
||||
}
|
||||
/* line 87, ../scss/hieroglyph.scss */
|
||||
slide.title-image div.figure .caption {
|
||||
color: black;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
padding: 0 5px;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* line 5, ../scss/io2013.scss */
|
||||
* {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* line 9, ../scss/io2013.scss */
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* line 12, ../scss/io2013.scss */
|
||||
h2, h3 {
|
||||
color: #515151;
|
||||
}
|
||||
|
||||
/* line 16, ../scss/io2013.scss */
|
||||
q, blockquote {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* line 20, ../scss/io2013.scss */
|
||||
slides > slide {
|
||||
color: #515151;
|
||||
}
|
||||
/* line 24, ../scss/io2013.scss */
|
||||
slides > slide.title-slide:after {
|
||||
content: '';
|
||||
background: url(../../images/io2013/google-io-lockup-1.png) no-repeat 100% 50%;
|
||||
-moz-background-size: contain;
|
||||
-o-background-size: contain;
|
||||
-webkit-background-size: contain;
|
||||
background-size: contain;
|
||||
position: absolute;
|
||||
bottom: 80px;
|
||||
right: 40px;
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
}
|
||||
/* line 36, ../scss/io2013.scss */
|
||||
slides > slide.title-slide hgroup h1 {
|
||||
font-weight: bold;
|
||||
line-height: 1.1;
|
||||
}
|
||||
/* line 40, ../scss/io2013.scss */
|
||||
slides > slide.title-slide hgroup h2, slides > slide.title-slide hgroup p {
|
||||
color: #515151;
|
||||
}
|
||||
/* line 43, ../scss/io2013.scss */
|
||||
slides > slide.title-slide hgroup h2 {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
/* line 46, ../scss/io2013.scss */
|
||||
slides > slide.title-slide hgroup p {
|
||||
margin-top: 3em;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*Smartphones (portrait and landscape) ----------- */
|
||||
/*@media only screen
|
||||
and (min-width : 320px)
|
||||
and (max-width : 480px) {
|
||||
|
||||
}*/
|
||||
/* Smartphones (portrait) ----------- */
|
||||
/* Styles */
|
||||
/* line 17, ../scss/phone.scss */
|
||||
slides > slide {
|
||||
/* width: $slide-width !important;
|
||||
height: $slide-height !important;
|
||||
margin-left: -$slide-width / 2 !important;
|
||||
margin-top: -$slide-height / 2 !important;
|
||||
*/
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
-webkit-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
/* iPhone 4 ----------- */
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5) {
|
||||
/* Styles */
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import "compass/reset";
|
||||
@import "compass/css3/border-radius";
|
||||
@import "compass/css3/box";
|
||||
@import "compass/css3/box-shadow";
|
||||
@import "compass/css3/box-sizing";
|
||||
@import "compass/css3/images";
|
||||
@import "compass/css3/text-shadow";
|
||||
@import "compass/css3/background-size";
|
||||
@import "compass/css3/transform";
|
||||
@import "compass/css3/transition";
|
||||
|
||||
@import "variables";
|
||||
|
||||
@mixin font-smoothing($val: antialiased) {
|
||||
-webkit-font-smoothing: $val;
|
||||
-moz-font-smoothing: $val;
|
||||
-ms-font-smoothing: $val;
|
||||
-o-font-smoothing: $val;
|
||||
}
|
||||
|
||||
@mixin flexbox {
|
||||
display: -webkit-box !important;
|
||||
display: -moz-box !important;
|
||||
display: -ms-box !important;
|
||||
display: -o-box !important;
|
||||
display: box !important;
|
||||
}
|
||||
|
||||
@mixin flex-center-center {
|
||||
@include box-orient(vertical);
|
||||
@include box-align(center);
|
||||
@include box-pack(center);
|
||||
}
|
||||
|
||||
@mixin flex-left-center {
|
||||
@include box-orient(vertical);
|
||||
@include box-align(left);
|
||||
@include box-pack(center);
|
||||
}
|
||||
|
||||
@mixin flex-right-center {
|
||||
@include box-orient(vertical);
|
||||
@include box-align(end);
|
||||
@include box-pack(center);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base SlideDeck Styles
|
||||
*/
|
||||
html {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
opacity: 0;
|
||||
|
||||
height: 100%;
|
||||
min-height: 740px;
|
||||
width: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
color: #fff;
|
||||
@include font-smoothing(antialiased);
|
||||
@include transition(opacity 800ms ease-in 100ms); // Add small delay to prevent jank.
|
||||
|
||||
&.loaded {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
input, button {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
slides > slide[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
slides {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
@include transform(translate3d(0, 0, 0));
|
||||
@include perspective(1000);
|
||||
@include transform-style(preserve-3d);
|
||||
@include transition(opacity 800ms ease-in 100ms); // Add small delay to prevent jank.
|
||||
}
|
||||
|
||||
slides > slide {
|
||||
display: block;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
@include box-sizing(border-box);
|
||||
}
|
||||
|
||||
/* Slide styles */
|
||||
|
||||
|
||||
/*article.fill iframe {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
border: 0;
|
||||
margin: 0;
|
||||
|
||||
@include border-radius(10px);
|
||||
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
slide.fill {
|
||||
background-repeat: no-repeat;
|
||||
@include background-size(cover);
|
||||
}
|
||||
|
||||
slide.fill img {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
z-index: -1;
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,34 @@
|
||||
$social-tags: '';
|
||||
$brand-small-icon-size: 30px;
|
||||
|
||||
$gray-1: #e6e6e6;
|
||||
$gray-2: #a9a9a9;
|
||||
$gray-3: #797979;
|
||||
$gray-4: #515151;
|
||||
|
||||
$brand-blue: rgb(67, 135, 253);
|
||||
$brand-blue-secondary: #3c8ef3;
|
||||
$brand-blue-secondary2: #2a7cdf;
|
||||
|
||||
$brand-red: rgb(244, 74, 63);
|
||||
$brand-red-secondary: #e0543e;
|
||||
$brand-red-secondary2: #d94d3a;
|
||||
|
||||
$brand-yellow: rgb(255, 209, 77);
|
||||
$brand-yellow-secondary: #f9cc46;
|
||||
$brand-yellow-secondary2: #f6c000;
|
||||
|
||||
$brand-green: rgb(13, 168, 97);
|
||||
$brand-green-secondary: #00a86d;
|
||||
$brand-green-secondary2: #009f5d;
|
||||
|
||||
$slide-width: 900px;
|
||||
$slide-height: 700px;
|
||||
$slide-width-widescreen: 1100px;
|
||||
$slide-top-bottom-padding: 40px;
|
||||
$slide-left-right-padding: 60px;
|
||||
$slide-border-radius: 5px;
|
||||
|
||||
$slide-tap-area-width: 100px;
|
||||
|
||||
$article-content-top-padding: 45px;
|
||||
@@ -0,0 +1,100 @@
|
||||
@import "compass/css3/background-size";
|
||||
|
||||
@import "variables";
|
||||
|
||||
ol {
|
||||
margin-left: 1.2em;
|
||||
margin-bottom: 1em;
|
||||
position: relative;
|
||||
list-style: decimal;
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
ol {
|
||||
margin-left: 2em;
|
||||
margin-bottom: 0;
|
||||
list-style: decimal;
|
||||
|
||||
li:before {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
margin-top: .5em;
|
||||
list-style: decimal;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
slide.title-image {
|
||||
|
||||
padding-right: 0px;
|
||||
|
||||
hgroup {
|
||||
position: static !important;
|
||||
|
||||
margin-top: 35%;
|
||||
padding-left: 30px;
|
||||
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
|
||||
border-top-left-radius: $slide-border-radius;
|
||||
-webkit-border-top-left-radius: $slide-border-radius;
|
||||
-moz-border-top-left-radius: $slide-border-radius;
|
||||
-o-border-top-left-radius: $slide-border-radius;
|
||||
}
|
||||
|
||||
hgroup + article {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
|
||||
margin-top: 0px;
|
||||
padding-left: 30px;
|
||||
|
||||
border-bottom-left-radius: $slide-border-radius;
|
||||
-webkit-border-bottom-left-radius: $slide-border-radius;
|
||||
-moz-border-bottom-left-radius: $slide-border-radius;
|
||||
-o-border-bottom-left-radius: $slide-border-radius;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #222;
|
||||
font-size: 3.2em;
|
||||
|
||||
line-height: 1.5em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
div.figure {
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
|
||||
border-radius: $slide-border-radius;
|
||||
-o-border-radius: $slide-border-radius;
|
||||
-moz-border-radius: $slide-border-radius;
|
||||
-webkit-border-radius: $slide-border-radius;
|
||||
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: black;
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
padding: 0 5px;
|
||||
border-bottom-left-radius: $slide-border-radius;
|
||||
border-top-right-radius: $slide-border-radius;
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
@import "compass/css3/background-size";
|
||||
|
||||
@import "variables";
|
||||
|
||||
* {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
}
|
||||
h2, h3 {
|
||||
color: $gray-4;
|
||||
}
|
||||
|
||||
q, blockquote {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
slides > slide {
|
||||
color: $gray-4;
|
||||
|
||||
&.title-slide {
|
||||
&:after {
|
||||
content: '';
|
||||
background: url(../../images/io2013/google-io-lockup-1.png) no-repeat 100% 50%;
|
||||
@include background-size(contain);
|
||||
position: absolute;
|
||||
bottom: $slide-top-bottom-padding + 40;
|
||||
right: $slide-top-bottom-padding;
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
hgroup {
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
line-height: 1.1;
|
||||
}
|
||||
h2, p {
|
||||
color: $gray-4;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
p {
|
||||
margin-top: 3em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
@import "compass/css3/transition";
|
||||
|
||||
|
||||
/*Smartphones (portrait and landscape) ----------- */
|
||||
/*@media only screen
|
||||
and (min-width : 320px)
|
||||
and (max-width : 480px) {
|
||||
|
||||
}*/
|
||||
|
||||
/* Smartphones (portrait) ----------- */
|
||||
//@media only screen and (max-device-width: 480px) {
|
||||
/* Styles */
|
||||
//$slide-width: 350px;
|
||||
//$slide-height: 500px;
|
||||
|
||||
slides > slide {
|
||||
/* width: $slide-width !important;
|
||||
height: $slide-height !important;
|
||||
margin-left: -$slide-width / 2 !important;
|
||||
margin-top: -$slide-height / 2 !important;
|
||||
*/
|
||||
// Don't do full slide transitions on mobile.
|
||||
-webkit-transition: none !important; // Bug in compass? Not sure why the below is not working
|
||||
@include transition(none !important);
|
||||
}
|
||||
|
||||
//}
|
||||
|
||||
/* iPhone 4 ----------- */
|
||||
@media
|
||||
only screen and (-webkit-min-device-pixel-ratio : 1.5),
|
||||
only screen and (min-device-pixel-ratio : 1.5) {
|
||||
/* Styles */
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = slides.css
|
||||
|
||||
[options]
|
||||
custom_css =
|
||||
custom_js =
|
||||
|
||||
subtitle =
|
||||
use_builds = true
|
||||
use_prettify = true
|
||||
enable_slide_areas = true
|
||||
enable_touch = true
|
||||
favicon = ''
|
||||
presenters =
|
||||
1
docs/codeql/ql-training/_static-training/title-slide.svg
Normal file
|
After Width: | Height: | Size: 98 KiB |
125
docs/codeql/ql-training/conf.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# CodeQL training slides build configuration file
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# For details of all possible config values,
|
||||
# see https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
##################################################################################
|
||||
|
||||
# -- GLOBAL GENERAL CONFIG VALUES ------------------------------------------------
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.7.9'
|
||||
# Beware that some of the extensions don't work in version 1.8
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.mathjax',
|
||||
'hieroglyph',
|
||||
'sphinx.ext.graphviz',
|
||||
]
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# Import the QL Lexer to use for syntax highlighting
|
||||
import sys
|
||||
import os
|
||||
|
||||
def setup(sphinx):
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir, 'global-sphinx-files')))
|
||||
from qllexer import QLLexer
|
||||
sphinx.add_lexer("ql", QLLexer())
|
||||
|
||||
# Set QL as the default language for highlighting code. Set to none to disable
|
||||
# syntax highlighting. If omitted or left blank, it defaults to Python 3.
|
||||
highlight_language = 'ql'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'CodeQL training and variant analysis examples'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static-training']
|
||||
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
#templates_path = ['../rst-styles/_templates-training']
|
||||
#slide_theme_options = {'custom_css':'custom.css'}
|
||||
slide_theme = 'slides-semmle-2'
|
||||
slide_theme_path = ["_static-training/"]
|
||||
|
||||
# -- Project-specifc options for HTML output ----------------------------------------------
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = 'CodeQL training and variant analysis examples'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'CodeQL training'
|
||||
# The Semmle version info for the current release you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'1.24'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'1.24'
|
||||
copyright = u'2019 Semmle Ltd'
|
||||
author = u'Semmle Ltd'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
language = None
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
html_show_sourcelink = False
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
html_show_sphinx = False
|
||||
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {'font_size': '16px',
|
||||
'body_text': '#333',
|
||||
'link': '#2F1695',
|
||||
'link_hover': '#2F1695',
|
||||
'font_family': 'Lato, sans-serif',
|
||||
'head_font_family': 'Moderat, sans-serif',
|
||||
'show_powered_by': False,
|
||||
'nosidebar':True,
|
||||
}
|
||||
|
||||
# Exclude the slide snippets from the build
|
||||
exclude_patterns = ['slide-snippets']
|
||||
|
||||
##############################################################################
|
||||
232
docs/codeql/ql-training/cpp/bad-overflow-guard.rst
Normal file
@@ -0,0 +1,232 @@
|
||||
===========================
|
||||
Example: Bad overflow guard
|
||||
===========================
|
||||
|
||||
CodeQL for C/C++
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `ChakraCore database <https://downloads.lgtm.com/snapshots/cpp/microsoft/chakracore/ChakraCore-revision-2017-April-12--18-13-26.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For the examples in this presentation, we will be analyzing `ChakraCore <https://github.com/microsoft/ChakraCore>`__.
|
||||
|
||||
You can query the project in `the query console <https://lgtm.com/query/project:2034240708/lang:cpp/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
Checking for overflow in C
|
||||
==========================
|
||||
|
||||
- Arithmetic operations overflow if the result is too large for their type.
|
||||
- Developers sometimes exploit this to write overflow checks:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
if (v + b < v) {
|
||||
handle_error("overflow");
|
||||
} else {
|
||||
result = v + b;
|
||||
}
|
||||
|
||||
Where might this go wrong?
|
||||
|
||||
.. note::
|
||||
|
||||
- In C/C++ we often need to check for whether an operation `overflows <https://en.wikipedia.org/wiki/Integer_overflow>`__.
|
||||
- An overflow is when an arithmetic operation, such as an addition, results in a number which is too large to be stored in the type.
|
||||
- When an operation overflows, the value “wraps” around.
|
||||
- A typical way to check for overflow of an addition, therefore, is whether the result is less than one of the arguments–that is the result has **wrapped**.
|
||||
|
||||
Integer promotion
|
||||
=================
|
||||
|
||||
From `https://en.cppreference.com/w/c/language/conversion <https://en.cppreference.com/w/c/language/conversion>`__:
|
||||
|
||||
.. rst-class:: quote
|
||||
|
||||
Integer promotion is the implicit conversion of a value of any integer type with rank less or equal to rank of ``int`` ... to the value of type ``int`` or ``unsigned int``.
|
||||
|
||||
- The arguments of the following arithmetic operators undergo implicit conversions:
|
||||
|
||||
- binary arithmetic ``* / % + -``
|
||||
- relational operators ``< > <= >= == !=``
|
||||
- binary bitwise operators ``& ^ |``
|
||||
- the conditional operator ``?:``
|
||||
|
||||
.. note::
|
||||
|
||||
- Most of the time integer conversion works fine. However, the rules governing addition in C/C++ are complex, and it's easy to get bitten.
|
||||
- CPUs generally prefer to perform arithmetic operations on 32-bit or larger integers, as the architectures are optimized to perform those efficiently.
|
||||
- The compiler therefore performs “integer promotion” for arguments to arithmetic operations that are smaller than 32-bit.
|
||||
|
||||
Checking for overflow in C revisited
|
||||
====================================
|
||||
|
||||
Which branch gets executed in this example? What is the value of ``result``?
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
uint16_t v = 65535;
|
||||
uint16_t b = 1;
|
||||
uint16_t result;
|
||||
if (v + b < v) {
|
||||
handle_error("overflow");
|
||||
} else {
|
||||
result = v + b;
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
In this example the second branch is executed, even though there is a 16-bit overflow, and ``result`` is set to zero.
|
||||
|
||||
Checking for overflow in C revisited
|
||||
====================================
|
||||
|
||||
Here is the example again, with the conversions made explicit:
|
||||
|
||||
.. code-block:: cpp
|
||||
:emphasize-lines: 4,7
|
||||
|
||||
uint16_t v = 65535;
|
||||
uint16_t b = 1;
|
||||
uint16_t result;
|
||||
if ((int)v + (int)b < (int)v) {
|
||||
handle_error("overflow");
|
||||
} else {
|
||||
result = (uint16_t)((int)v + (int)b);
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
In this example the second branch is executed, even though there is a 16-bit overflow, and result is set to zero.
|
||||
|
||||
Explanation:
|
||||
|
||||
- The two integer arguments to the addition, ``v`` and ``b``, are promoted to 32-bit integers.
|
||||
- The comparison (``<``) is also an arithmetic operation, therefore it will also be completed on 32-bit integers.
|
||||
- This means that ``v + b < v`` will never be true, because v and b can hold at most 2 :sup:`16`.
|
||||
- Therefore, the second branch is executed, but the result of the addition is stored into the result variable. Overflow will still occur as result is a 16-bit integer.
|
||||
|
||||
This happens even though the overflow check passed!
|
||||
|
||||
.. rst-class:: background2
|
||||
|
||||
Developing a CodeQL query
|
||||
=========================
|
||||
|
||||
Finding bad overflow guards
|
||||
|
||||
CodeQL query: bad overflow guards
|
||||
==================================
|
||||
|
||||
Let’s look for overflow guards of the form ``v + b < v``, using the classes
|
||||
``AddExpr``, ``Variable`` and ``RelationalOperation`` from the ``cpp`` library.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/bad-overflow-guard-1.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
- When performing variant analysis, it is usually helpful to write a simple query that finds the simple syntactic pattern, before trying to go on to describe the cases where it goes wrong.
|
||||
- In this case, we start by looking for all the *overflow* checks, before trying to refine the query to find all *bad overflow* checks.
|
||||
- The ``select`` clause defines what this query is looking for:
|
||||
|
||||
- an ``AddExpr``: the expression that is being checked for overflow.
|
||||
- a ``RelationalOperation``: the overflow comparison check.
|
||||
- a ``Variable``: used as an argument to both the addition and comparison.
|
||||
|
||||
- The ``where`` part of the query ties these three variables together using `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ defined in the `standard CodeQL for C/C++ library <https://help.semmle.com/qldoc/cpp/>`__.
|
||||
|
||||
CodeQL query: bad overflow guards
|
||||
=================================
|
||||
|
||||
We want to ensure the operands being added have size less than 4 bytes.
|
||||
|
||||
We may want to reuse this logic, so let us create a separate predicate.
|
||||
|
||||
Looking at autocomplete suggestions, we see that we can get the type of an expression using the ``getType()`` method.
|
||||
|
||||
We can get the size (in bytes) of a type using the ``getSize()`` method.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isSmall(Expr e) {
|
||||
e.getType().getSize() < 4
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
- An important part of the query is to determine whether a given expression has a “small” type that is going to trigger integer promotion.
|
||||
- We therefore write a helper predicate for small expressions.
|
||||
- This predicate effectively represents the set of all expressions in the database where the size of the type of the expression is less than 4 bytes, that is, less than 32-bits.
|
||||
|
||||
CodeQL query: bad overflow guards
|
||||
==================================
|
||||
|
||||
We can ensure the operands being added have size less than 4 bytes, using our new predicate.
|
||||
|
||||
QL has logical quantifiers like ``exists`` and ``forall``, allowing us to declare variables and enforce a certain condition on them.
|
||||
|
||||
Now our query becomes:
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/bad-overflow-guard-2.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
- Recall from earlier that what makes an overflow check a “bad” check is that all the arguments to the addition are integers smaller than 32-bits.
|
||||
- We could write this by using our helper predicate ``isSmall`` to specify that each individual operand to the addition ``isSmall`` (that is under 32-bits):
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
isSmall(a.getLeftOperand()) and
|
||||
isSmall(a.getRightOperand())
|
||||
|
||||
- However, this is a little bit repetitive. What we really want to say is that: all the operands of the addition are small. Fortunately, QL provides a ``forall`` formula that we can use in these circumstances.
|
||||
- A ``forall`` has three parts:
|
||||
|
||||
- A “declaration” part, where we can introduce variables.
|
||||
- A “range” part, which allows us to restrict those variables.
|
||||
- A “condition” part. The ``forall`` as a whole holds if the condition holds for each of the values in the range.
|
||||
- In our case:
|
||||
|
||||
- The declaration introduces a variable for expressions, called ``op``. At this stage, this variable represents all the expressions in the program.
|
||||
- The “range” part, ``op = a.getAnOperand()``, restricts ``op`` to being one of the two operands to the addition.
|
||||
- The “condition” part, ``isSmall(op)``, says that the ``forall`` holds only if the condition (that the ``op`` is small) holds for everything in the range–that is, both the arguments to the addition.
|
||||
|
||||
CodeQL query: bad overflow guards
|
||||
=================================
|
||||
|
||||
Sometimes the result of the addition is cast to a small type of size less than 4 bytes, preventing automatic widening. We don’t want our query to flag these instances.
|
||||
|
||||
We can use predicate ``Expr.getExplicitlyConverted()`` to reason about casts that are applied to an expression, adding this restriction to our query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
not isSmall(a.getExplicitlyConverted())
|
||||
|
||||
The final query
|
||||
===============
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/bad-overflow-guard-3.ql
|
||||
:language: ql
|
||||
|
||||
This query finds a single result in our historic database, which was `a genuine bug in ChakraCore <https://github.com/Microsoft/ChakraCore/commit/2500e1cdc12cb35af73d5c8c9b85656aba6bab4d>`__.
|
||||
367
docs/codeql/ql-training/cpp/control-flow-cpp.rst
Normal file
@@ -0,0 +1,367 @@
|
||||
======================
|
||||
Analyzing control flow
|
||||
======================
|
||||
|
||||
CodeQL for C/C++
|
||||
|
||||
.. Include information slides here
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `ChakraCore database <https://downloads.lgtm.com/snapshots/cpp/microsoft/chakracore/ChakraCore-revision-2017-April-12--18-13-26.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For the examples in this presentation, we will be analyzing `ChakraCore <https://github.com/microsoft/ChakraCore>`__.
|
||||
|
||||
You can query the project in `the query console <https://lgtm.com/query/project:2034240708/lang:cpp/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- Control flow graphs
|
||||
- Exercise: use after free
|
||||
- Recursion over the control flow graph
|
||||
- Basic blocks
|
||||
- Guard conditions
|
||||
|
||||
Control flow graphs
|
||||
===================
|
||||
|
||||
.. container:: column-left
|
||||
|
||||
We frequently want to ask questions about the possible *order of execution* for a program.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
if (x) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
|
||||
.. container:: column-right
|
||||
|
||||
Possible execution order is usually represented by a *control flow graph*:
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
graph [ dpi = 1000 ]
|
||||
node [shape=polygon,sides=4,color=blue4,style="filled,rounded",fontname=consolas,fontcolor=white]
|
||||
a [label=<if<BR /><FONT POINT-SIZE="10">IfStmt</FONT>>]
|
||||
b [label=<x<BR /><FONT POINT-SIZE="10">VariableAccess</FONT>>]
|
||||
c [label=<1<BR /><FONT POINT-SIZE="10">Literal</FONT>>]
|
||||
d [label=<2<BR /><FONT POINT-SIZE="10">Literal</FONT>>]
|
||||
e [label=<return<BR /><FONT POINT-SIZE="10">ReturnStmt</FONT>>]
|
||||
f [label=<return<BR /><FONT POINT-SIZE="10">ReturnStmt</FONT>>]
|
||||
|
||||
a -> b
|
||||
b -> {c, d}
|
||||
c -> e
|
||||
d -> f
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The control flow graph is a static over-approximation of possible control flow at runtime.
|
||||
Its nodes are program elements such as expressions and statements.
|
||||
If there is an edge from one node to another, then it means that the semantic operation corresponding to the first node may be immediately followed by the operation corresponding to the second node.
|
||||
Some nodes (such as conditions of “if” statements or loop conditions) have more than one successor, representing conditional control flow at runtime.
|
||||
|
||||
Modeling control flow
|
||||
=====================
|
||||
|
||||
The control flow is modeled with a CodeQL class, ``ControlFlowNode``. Examples of control flow nodes include statements and expressions.
|
||||
|
||||
- ``ControlFlowNode`` provides API for traversing the control flow graph:
|
||||
|
||||
- ``ControlFlowNode ControlFlowNode.getASuccessor()``
|
||||
- ``ControlFlowNode ControlFlowNode.getAPredecessor()``
|
||||
- ``ControlFlowNode ControlFlowNode.getATrueSuccessor()``
|
||||
- ``ControlFlowNode ControlFlowNode.getAFalseSuccessor()``
|
||||
|
||||
The control-flow graph is *intra-procedural*–in other words, only models paths within a function. To find the associated function, use
|
||||
|
||||
- ``Function ControlFlowNode.getControlFlowScope()``
|
||||
|
||||
.. note::
|
||||
|
||||
The control flow graph is similar in concept to data flow graphs. In contrast to data flow, however, the AST nodes are directly control flow graph nodes.
|
||||
|
||||
The predecessor/successor predicates are prime examples of member predicates with results that are used in functional syntax, but that are not actually functions. This is because a control flow node may have any number of predecessors and successors (including zero or more than one).
|
||||
|
||||
Example: malloc/free pairs
|
||||
==========================
|
||||
|
||||
Find calls to ``free`` that are reachable from an allocation on the same variable:
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/control-flow-cpp-1.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
Predicates ``allocationCall`` and ``freeCall`` are defined in the standard library and model a number of standard alloc/free-like functions.
|
||||
|
||||
Exercise: use after free
|
||||
========================
|
||||
|
||||
Based on this query, write a query that finds accesses to the variable that occur after the free.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
- What do you find? What problems occur with this approach to detecting use-after-free vulnerabilities?
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/control-flow-cpp-2.ql
|
||||
:language: ql
|
||||
|
||||
Utilizing recursion
|
||||
===================
|
||||
|
||||
The main problem we observed in the previous exercise was that the successor's relation is unaware of changes to the variable that would invalidate our results.
|
||||
|
||||
We can fix this by writing our own successor predicate that stops traversing the CFG if the variable is re-defined.
|
||||
|
||||
Utilizing recursion
|
||||
===================
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
ControlFlowNode reachesWithoutReassignment(FunctionCall free, LocalScopeVariable v)
|
||||
{
|
||||
freeCall(free, v.getAnAccess()) and
|
||||
(
|
||||
// base case
|
||||
result = free
|
||||
or
|
||||
// recursive case
|
||||
exists(ControlFlowNode mid |
|
||||
mid = reachesWithoutReassignment(free, v) and
|
||||
result = mid.getASuccessor() and
|
||||
// stop tracking when the value may change
|
||||
not result = v.getAnAssignedValue() and
|
||||
not result.(AddressOfExpr).getOperand() = v.getAnAccess()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Exercise
|
||||
========
|
||||
|
||||
Find local variables that are written to, and then never accessed again.
|
||||
|
||||
**Hint**: Use ``LocalVariable.getAnAssignment()``.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/control-flow-cpp-3.ql
|
||||
:language: ql
|
||||
|
||||
.. rst-class:: background2
|
||||
|
||||
More control flow
|
||||
=================
|
||||
|
||||
Basic blocks
|
||||
============
|
||||
|
||||
``BasicBlock`` represents basic blocks, that is, straight-line sequences of control flow nodes without branching.
|
||||
|
||||
- ``ControlFlowNode BasicBlock.getNode(int)``
|
||||
- ``BasicBlock BasicBlock.getASuccessor()``
|
||||
- ``BasicBlock BasicBlock.getAPredecessor()``
|
||||
- ``BasicBlock BasicBlock.getATrueSuccessor()``
|
||||
- ``BasicBlock BasicBlock.getAFalseSuccessor()``
|
||||
|
||||
Often, queries can be made more efficient by treating basic blocks as a unit instead of reasoning about individual control flow nodes.
|
||||
|
||||
Exercise: unreachable blocks
|
||||
============================
|
||||
|
||||
Write a query to find unreachable basic blocks.
|
||||
|
||||
**Hint**: First define a recursive predicate to identify reachable blocks. Class ``EntryBasicBlock`` may be useful.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/control-flow-cpp-4.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
This query has a good number of false positives on Chakra, many of them to do with templating and macros.
|
||||
|
||||
Guard conditions
|
||||
================
|
||||
|
||||
A ``GuardCondition`` is a ``Boolean`` condition that controls one or more basic blocks in the sense that it is known to be true/false at the entry of those blocks.
|
||||
|
||||
- ``GuardCondition.controls(BasicBlock bb, boolean outcome):`` the entry of bb can only be reached if the guard evaluates to outcome
|
||||
|
||||
- ``GuardCondition.comparesLt, GuardCondition.ensuresLt, GuardCondition.comparesEq:`` auxiliary predicates to identify conditions that guarantee that one expression is less than/equal to another
|
||||
|
||||
Further materials
|
||||
=================
|
||||
|
||||
- CodeQL for C/C++: https://help.semmle.com/QL/learn-ql/ql/cpp/ql-for-cpp.html
|
||||
- API reference: https://help.semmle.com/qldoc/cpp
|
||||
|
||||
.. rst-class:: end-slide
|
||||
|
||||
Extra slides
|
||||
============
|
||||
|
||||
.. rst-class:: background2
|
||||
|
||||
Appendix: Library customizations
|
||||
================================
|
||||
|
||||
Call graph customizations
|
||||
=========================
|
||||
|
||||
The default implementation of call target resolution does not handle function pointers, because they are difficult to deal with in general.
|
||||
|
||||
We can, however, add support for particular patterns of use by contributing a new override of ``Call.getTarget``.
|
||||
|
||||
Exercise: unresolvable calls
|
||||
============================
|
||||
|
||||
Write a query that finds all calls for which no call target can be determined, and run it on libjpeg-turbo.
|
||||
|
||||
Examine the results. What do you notice?
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import cpp
|
||||
|
||||
from Call c
|
||||
where not exists(c.getTarget())
|
||||
select c
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
- Many results are calls through struct fields emulating virtual dispatch.
|
||||
|
||||
Exercise: resolving calls through variables
|
||||
===========================================
|
||||
|
||||
Write a query that resolves the call at `cjpeg.c:640 <https://github.com/libjpeg-turbo/libjpeg-turbo/blob/9bc8eb6449a32f452ab3fc9f94af672a0af13f81/cjpeg.c#L640>`__.
|
||||
|
||||
**Hint**: Use classes ``ExprCall``, ``PointerDereferenceExpr``, and ``Access``.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/control-flow-cpp-5.ql
|
||||
:language: ql
|
||||
|
||||
Exercise: customizing the call graph
|
||||
====================================
|
||||
|
||||
Create a subclass of ``ExprCall`` that uses your query to implement ``getTarget``.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CallThroughVariable extends ExprCall {
|
||||
Variable v;
|
||||
CallThroughVariable() {
|
||||
exists(PointerDereferenceExpr callee | callee = getExpr() |
|
||||
callee.getOperand() = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
override Function getTarget() {
|
||||
result = super.getTarget() or
|
||||
exists(Access init | init = v.getAnAssignedValue() |
|
||||
result = init.getTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Control-flow graph customizations
|
||||
=================================
|
||||
|
||||
The default control-flow graph implementation recognizes a few common patterns for non-returning functions, but sometimes it fails to spot them, which can cause imprecision.
|
||||
|
||||
We can add support for new non-returning functions by overriding ``ControlFlowNode.getASuccessor()``.
|
||||
|
||||
Exercise: calls to ``error_exit``
|
||||
=================================
|
||||
|
||||
Write a query that finds all calls to a field called ``error_exit``.
|
||||
|
||||
**Hint**: Reuse (parts of) the ``CallThroughVariable`` class from before.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CallThroughVariable extends ExprCall { ... }
|
||||
|
||||
class ErrorExitCall extends CallThroughVariable {
|
||||
override Field v;
|
||||
|
||||
ErrorExitCall() { v.getName() = "error_exit" }
|
||||
}
|
||||
|
||||
from ErrorExitCall eec
|
||||
select eec
|
||||
|
||||
Exercise: customizing the control-flow graph
|
||||
============================================
|
||||
|
||||
Override ``ControlFlowNode`` to mark calls to ``error_exit`` as non-returning.
|
||||
|
||||
**Hint**: ``ExprCall`` is an indirect subclass of ``ControlFlowNode``.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CallThroughVariable extends ExprCall { ... }
|
||||
|
||||
class ErrorExitCall extends CallThroughVariable {
|
||||
override Field v;
|
||||
|
||||
ErrorExitCall() { v.getName() = "error_exit" }
|
||||
|
||||
override ControlFlowNode getASuccessor() { none() }
|
||||
}
|
||||
|
||||
``CustomOptions`` class
|
||||
=======================
|
||||
|
||||
The Options library defines a ``CustomOptions`` class with various member predicates that can be overridden to customize aspects of the analysis.
|
||||
|
||||
In particular, it has an ``exprExits`` predicate that can be overridden to more easily perform the customization on the previous slide:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import Options
|
||||
|
||||
class MyOptions extends CustomOptions {
|
||||
override predicate exprExits(Expr e) {
|
||||
super.exprExits(e) or ...
|
||||
}
|
||||
}
|
||||
195
docs/codeql/ql-training/cpp/data-flow-cpp.rst
Normal file
@@ -0,0 +1,195 @@
|
||||
=========================
|
||||
Introduction to data flow
|
||||
=========================
|
||||
|
||||
Finding string formatting vulnerabilities in C/C++
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `dotnet/coreclr database <http://downloads.lgtm.com/snapshots/cpp/dotnet/coreclr/dotnet_coreclr_fbe0c77.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For the examples in this presentation, we will be analyzing `dotnet/coreclr <https://github.com/dotnet/coreclr>`__.
|
||||
|
||||
You can query the project in `the query console <https://lgtm.com/query/projects:1505958977333/lang:cpp/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- Non-constant format string
|
||||
- Data flow
|
||||
- Modules and libraries
|
||||
- Local data flow
|
||||
- Local taint tracking
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
Let’s write a query to identify instances of `CWE-134 <https://cwe.mitre.org/data/definitions/134.html>`__ **Use of externally controlled format string**.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
printf(userControlledString, arg1);
|
||||
|
||||
**Goal**: Find uses of ``printf`` (or similar) where the format string can be controlled by an attacker.
|
||||
|
||||
.. note::
|
||||
|
||||
Formatting functions allow the programmer to construct a string output using a *format string* and an optional set of arguments. The *format string* is specified using a simple template language, where the output string is constructed by processing the format string to find *format specifiers*, and inserting values provided as arguments. For example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
printf("Name: %s, Age: %d", "Freddie", 2);
|
||||
|
||||
would produce the output ``"Name: Freddie, Age: 2”``. So far, so good. However, problems arise if there is a mismatch between the number of formatting specifiers, and the number of arguments. For example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
printf("Name: %s, Age: %d", "Freddie");
|
||||
|
||||
In this case, we have one more format specifier than we have arguments. In a managed language such as Java or C#, this simply leads to a runtime exception. However, in C/C++, the formatting functions are typically implemented by reading values from the stack without any validation of the number of arguments. This means a mismatch in the number of format specifiers and format arguments can lead to information disclosure.
|
||||
|
||||
Of course, in practice this happens rarely with *constant* formatting strings. Instead, it’s most problematic when the formatting string can be specified by the user, allowing an attacker to provide a formatting string with the wrong number of format specifiers. Furthermore, if an attacker can control the format string, they may be able to provide the ``%n`` format specifier, which causes ``printf`` to write the number characters in the generated output string to a specified location.
|
||||
|
||||
See https://en.wikipedia.org/wiki/Uncontrolled_format_string for more background.
|
||||
|
||||
Exercise: Non-constant format string
|
||||
====================================
|
||||
|
||||
Write a query that flags ``printf`` calls where the format argument is not a ``StringLiteral``.
|
||||
|
||||
**Hint**: Import ``semmle.code.cpp.commons.Printf`` and use class ``FormattingFunction`` and ``getFormatParameterIndex()``.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/data-flow-cpp-1.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
This first query is about finding places where the format specifier is not a constant string. In the CodeQL libraries for C/C++, constant strings are modeled as ``StringLiteral`` nodes, so we are looking for calls to format functions where the format specifier argument is not a string literal.
|
||||
|
||||
The `C/C++ standard libraries <https://help.semmle.com/qldoc/cpp/>`__ include many different formatting functions that may be vulnerable to this particular attack–including ``printf``, ``snprintf``, and others. Furthermore, each of these different formatting functions may include the format string in a different position in the argument list. Instead of laboriously listing all these different variants, we can make use of the standard CodeQL class ``FormattingFunction``, which provides an interface that models common formatting functions in C/C++.
|
||||
|
||||
Meh...
|
||||
======
|
||||
|
||||
Results are unsatisfactory:
|
||||
|
||||
- Query flags cases where the format string is a symbolic constant.
|
||||
- Query flags cases where the format string is itself a format argument.
|
||||
- Query doesn't recognize wrapper functions around ``printf``-like functions.
|
||||
|
||||
We need something better.
|
||||
|
||||
.. note::
|
||||
|
||||
For example, consider the results which appear in ``/src/ToolBox/SOS/Strike/util.h``, between lines 965 and 970:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
const char *format = align == AlignLeft ? "%-*.*s" : "%*.*s";
|
||||
|
||||
if (IsDMLEnabled())
|
||||
DMLOut(format, width, precision, mValue);
|
||||
else
|
||||
ExtOut(format, width, precision, mValue);
|
||||
|
||||
Here, ``DMLOut`` and ``ExtOut`` are macros that expand to formatting calls. The format specifier is not constant, in the sense that the format argument is not a string literal. However, it is clearly one of two possible constants, both with the same number of format specifiers.
|
||||
|
||||
What we need is a way to determine whether the format argument is ever set to something that is not constant.
|
||||
|
||||
.. include general data flow slides
|
||||
|
||||
.. include:: ../slide-snippets/local-data-flow.rst
|
||||
|
||||
.. resume language-specific slides
|
||||
|
||||
Exercise: source nodes
|
||||
======================
|
||||
|
||||
Define a subclass of ``DataFlow::Node`` representing “source” nodes, that is, nodes without a (local) data flow predecessor.
|
||||
|
||||
**Hint**: use ``not exists()``.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class SourceNode extends DataFlow::Node {
|
||||
SourceNode() {
|
||||
not DataFlow::localFlowStep(_, this)
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
Note the scoping of the `don’t-care variable <https://help.semmle.com/QL/ql-handbook/expressions.html#don-t-care-expressions>`__ “_” in this example: the body of the characteristic predicate is equivalent to:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
not exists(DataFlow::Node pred | DataFlow::localFlowStep(pred, this))
|
||||
|
||||
which is not the same as:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
exists(DataFlow::Node pred | not DataFlow::localFlowStep(pred, this)).
|
||||
|
||||
Revisiting non-constant format strings
|
||||
======================================
|
||||
|
||||
Refine the query to find calls to ``printf``-like functions where the format argument derives from a local source that is not a constant string.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.commons.Printf
|
||||
|
||||
class SourceNode extends DataFlow::Node { ... }
|
||||
|
||||
from FormattingFunction f, Call c, SourceNode src, DataFlow::Node arg
|
||||
where c.getTarget() = f and
|
||||
arg.asExpr() = c.getArgument(f.getFormatParameterIndex()) and
|
||||
DataFlow::localFlow(src, arg) and
|
||||
not src.asExpr() instanceof StringLiteral
|
||||
select arg, "Non-constant format string."
|
||||
|
||||
Refinements (take home exercise)
|
||||
================================
|
||||
|
||||
Audit the results and apply any refinements you deem necessary.
|
||||
|
||||
Suggestions:
|
||||
|
||||
- Replace ``DataFlow::localFlowStep`` with a custom predicate that includes steps through global variable definitions.
|
||||
|
||||
**Hint**: Use class ``GlobalVariable`` and its member predicates ``getAnAssignedValue()`` and ``getAnAccess()``.
|
||||
|
||||
- Exclude calls in wrapper functions that just forward their format argument to another ``printf``-like function; instead, flag calls to those functions.
|
||||
|
||||
Beyond local data flow
|
||||
======================
|
||||
|
||||
- Results are still underwhelming.
|
||||
- Dealing with parameter passing becomes cumbersome.
|
||||
- Instead, let’s turn the problem around and find user-controlled data that flows into a ``printf`` format argument, potentially through calls.
|
||||
- This needs :doc:`global data flow <global-data-flow-cpp>`.
|
||||
191
docs/codeql/ql-training/cpp/global-data-flow-cpp.rst
Normal file
@@ -0,0 +1,191 @@
|
||||
================================
|
||||
Introduction to global data flow
|
||||
================================
|
||||
|
||||
CodeQL for C/C++
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `dotnet/coreclr database <http://downloads.lgtm.com/snapshots/cpp/dotnet/coreclr/dotnet_coreclr_fbe0c77.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For the examples in this presentation, we will be analyzing `dotnet/coreclr <https://github.com/dotnet/coreclr>`__.
|
||||
|
||||
You can query the project in `the query console <https://lgtm.com/query/projects:1505958977333/lang:cpp/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- Global taint tracking
|
||||
- Sanitizers
|
||||
- Path queries
|
||||
- Data flow models
|
||||
|
||||
.. insert common global data flow slides
|
||||
|
||||
.. include:: ../slide-snippets/global-data-flow.rst
|
||||
|
||||
.. resume language-specific global data flow slides
|
||||
|
||||
|
||||
Finding tainted format strings (outline)
|
||||
========================================
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/global-data-flow-cpp-1.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
Here’s the outline for a inter-procedural (that is, “global”) version of the tainted formatting strings query we saw in the previous slide deck. The same template will be applicable for most taint tracking problems.
|
||||
|
||||
Defining sources
|
||||
================
|
||||
|
||||
The library class ``SecurityOptions`` provides a (configurable) model of what counts as user-controlled data:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import semmle.code.cpp.security.Security
|
||||
|
||||
class TaintedFormatConfig extends TaintTracking::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists (SecurityOptions opts |
|
||||
opts.isUserInput(source.asExpr(), _)
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
We first define what it means to be a *source* of tainted data for this particular problem. In this case, what we care about is whether the format string can be provided by an external user to our application or service. As there are many such ways external data could be introduced into the system, the standard CodeQL libraries for C/C++ include an extensible API for modeling user input. In this case, we will simply use the predefined set of *user inputs*, which includes arguments provided to command line applications.
|
||||
|
||||
|
||||
Defining sinks (exercise)
|
||||
=========================
|
||||
|
||||
Use the ``FormattingFunction`` class to fill in the definition of ``isSink``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import semmle.code.cpp.security.Security
|
||||
|
||||
class TaintedFormatConfig extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
/* Fill me in */
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The second part is to define what it means to be a sink for this particular problem. The queries from the previous slide deck will be useful for this exercise.
|
||||
|
||||
Defining sinks (answer)
|
||||
=======================
|
||||
|
||||
Use the ``FormattingFunction`` class, we can write the sink as:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import semmle.code.cpp.security.Security
|
||||
|
||||
class TaintedFormatConfig extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists (FormattingFunction ff, Call c |
|
||||
c.getTarget() = ff and
|
||||
c.getArgument(ff.getFormatParameterIndex()) = sink.asExpr()
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
When we run this query, we should find a single result. However, it is tricky to determine whether this result is a true positive (a “real” result) because our query only reports the source and the sink, and not the path through the graph between the two.
|
||||
|
||||
.. insert path queries slides
|
||||
|
||||
.. include:: ../slide-snippets/path-queries.rst
|
||||
|
||||
.. resume language-specific global data flow slides
|
||||
|
||||
Defining additional taint steps
|
||||
===============================
|
||||
|
||||
Add an additional taint step that (heuristically) taints a local variable if it is a pointer, and it is passed to a function in a parameter position that taints it.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TaintedFormatConfig extends TaintTracking::Configuration {
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred,
|
||||
DataFlow::Node succ) {
|
||||
exists (Call c, Expr arg, LocalVariable lv |
|
||||
arg = c.getAnArgument() and
|
||||
arg = pred.asExpr() and
|
||||
arg.getFullyConverted().getUnderlyingType() instanceof PointerType and
|
||||
arg = lv.getAnAccess() and
|
||||
succ.asUninitialized() = lv
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
Defining sanitizers
|
||||
===================
|
||||
|
||||
Add a sanitizer, stopping propagation at parameters of formatting functions, to avoid double-reporting:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TaintedFormatConfig extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node nd) {
|
||||
exists (FormattingFunction ff, int idx |
|
||||
idx = ff.getFormatParameterIndex() and
|
||||
nd = DataFlow::parameterNode(ff.getParameter(idx))
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
Data flow models
|
||||
================
|
||||
|
||||
- To provide models of data/taint flow through library functions, you can implement subclasses of ``DataFlowFunction`` (from ``semmle.code.cpp.models.interfaces.DataFlow``) and ``TaintFunction`` (from ``semmle.code.cpp.models.interfaces.Taint``), respectively
|
||||
|
||||
- Example: model of taint flow from third to first parameter of ``memcpy``
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class MemcpyFunction extends TaintFunction {
|
||||
MemcpyFunction() { this.hasName("memcpy") }
|
||||
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o)
|
||||
i.isInParameter(2) and o.isOutParameterPointer(0)
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
See the API documentation for more details about ``FunctionInput`` and ``FunctionOutput``.
|
||||
|
||||
.. rst-class:: end-slide
|
||||
|
||||
Extra slides
|
||||
============
|
||||
|
||||
.. include:: ../slide-snippets/global-data-flow-extra-slides.rst
|
||||
208
docs/codeql/ql-training/cpp/intro-ql-cpp.rst
Normal file
@@ -0,0 +1,208 @@
|
||||
================================
|
||||
Introduction to variant analysis
|
||||
================================
|
||||
|
||||
CodeQL for C/C++
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `exiv2 database <http://downloads.lgtm.com/snapshots/cpp/exiv2/Exiv2_exiv2_b090f4d.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `exiv2 <https://github.com/Exiv2/exiv2>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:1506532406873/lang:cpp/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
.. Include language-agnostic section here
|
||||
|
||||
.. include:: ../slide-snippets/intro-ql-general.rst
|
||||
|
||||
Oops
|
||||
====
|
||||
|
||||
.. code-block:: cpp
|
||||
:emphasize-lines: 3
|
||||
|
||||
int write(int buf[], int size, int loc, int val) {
|
||||
if (loc >= size) {
|
||||
// return -1;
|
||||
}
|
||||
|
||||
buf[loc] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- The return statement has been commented out (during debugging?)
|
||||
- The if statement is now dead code
|
||||
- No bounds checking!
|
||||
|
||||
.. note::
|
||||
|
||||
Here’s a simple (artificial) bug, which we’ll develop a query to catch.
|
||||
|
||||
This function writes a value to a given location in an array, first trying to do a bounds check to validate that the location is within bounds. However, the return statement has been commented out, leaving a redundant if statement and no bounds checking.
|
||||
|
||||
This case can act as our “patient zero” in the variant analysis game.
|
||||
|
||||
A simple CodeQL query
|
||||
=====================
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/empty-if-cpp.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
We are going to write a simple query which finds “if statements” with empty “then” blocks, so we can highlight the results like those on the previous slide. The query can be run in the `query console on LGTM <https://lgtm.com/query>`__, or in your `IDE <https://lgtm.com/help/lgtm/running-queries-ide>`__.
|
||||
|
||||
A `query <https://help.semmle.com/QL/ql-handbook/queries.html>`__ consists of a “select” clause that indicates what results should be returned. Typically it will also provide a “from” clause to declare some variables, and a “where” clause to state conditions over those variables. For more information on the structure of query files (including links to useful topics in the `QL language reference <https://help.semmle.com/QL/ql-handbook/index.html>`__), see `About CodeQL queries <https://help.semmle.com/QL/learn-ql/ql/writing-queries/introduction-to-queries.html>`__.
|
||||
|
||||
In our example here, the first line of the query imports the `CodeQL library for C/C++ <https://help.semmle.com/qldoc/cpp/>`__, which defines concepts like ``IfStmt`` and ``Block``.
|
||||
The query proper starts by declaring two variables–ifStmt and block. These variables represent sets of values in the database, according to the type of each of the variables. For example, ifStmt has the type IfStmt, which means it represents the set of all if statements in the program.
|
||||
|
||||
If we simply selected these two variables::
|
||||
|
||||
from IfStmt ifStmt, Block block
|
||||
select ifStmt, block
|
||||
|
||||
We would get a result row for every combination of blocks and if statements in the program. This is known as a cross-product, because there is no logical condition linking the two variables. We can use the where clause to specify the condition that we are only interested in rows where the “block” is the “then” part of the if statement. We do this by specifying::
|
||||
|
||||
block = ifStmt.getThen()
|
||||
|
||||
This states that the block is equal to (not assigned!) the “then” part of the ``ifStmt``. ``getThen()`` is an operation which is available on any IfStmt. One way to interpret this is as a filtering operation – starting with every pair of block and if statements, check each one to whether the logical condition holds, and only keep the row if that is the case.
|
||||
We can add a second condition that specifies the block must be empty::
|
||||
|
||||
and block.isEmpty()
|
||||
|
||||
The ``isEmpty()`` operation is available on any Block, and is only true if the “block” has no children.
|
||||
|
||||
Finally, we select a location, at which to report the problem, and a message, to explain what the problem is.
|
||||
|
||||
|
||||
|
||||
Structure of a query
|
||||
====================
|
||||
|
||||
A **query file** has the extension ``.ql`` and contains a **query clause**, and optionally **predicates**, **classes**, and **modules**.
|
||||
|
||||
A **query library** has the extension ``.qll`` and does not contain a query clause, but may contain modules, classes, and predicates.
|
||||
|
||||
Each query library also implicitly defines a module.
|
||||
|
||||
**Import** statements allow the classes and predicates defined in one module to be used in another.
|
||||
|
||||
.. note::
|
||||
|
||||
Queries are always contained in query files with the file extension ``.ql``.
|
||||
|
||||
Parts of queries can be lifted into `library files <https://help.semmle.com/QL/ql-handbook/modules.html#library-modules>`__ with the extension ``.qll``. Definitions within such libraries can be brought into scope using ``import`` statements, and similarly QLL files can import each other’s definitions using “import” statements.
|
||||
|
||||
Logic can be encapsulated as user-defined `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ and `classes <https://help.semmle.com/QL/ql-handbook/types.html#classes>`__, and organized into `modules <https://help.semmle.com/QL/ql-handbook/modules.html>`__. Each QLL file implicitly defines a module, but QL and QLL files can also contain explicit module definitions, as we will see later.
|
||||
|
||||
Predicates
|
||||
==========
|
||||
|
||||
A predicate allows you to pull out and name parts of a query.
|
||||
|
||||
.. container:: column-left
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/empty-if-cpp.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 6
|
||||
|
||||
.. container:: column-right
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/empty-if-cpp-predicate.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 3-5
|
||||
|
||||
.. note::
|
||||
|
||||
A `predicate <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ takes zero or more parameters, and its body is a condition on those parameters. The predicate may (or may not) hold. Predicates may also be `recursive <https://help.semmle.com/QL/ql-handbook/predicates.html#recursive-predicates>`__, simply by referring to themselves (directly or indirectly).
|
||||
|
||||
You can imagine a predicate to be a self-contained from-where-select statement, that produces an intermediate relation, or table. In this case, the ``isEmpty`` predicate will be the set of all blocks which are empty.
|
||||
|
||||
Classes in QL
|
||||
=============
|
||||
|
||||
A QL class allows you to name a set of values and define (member) predicates on them.
|
||||
|
||||
A class has at least one supertype and optionally a **characteristic predicate**; it contains the values that belong to *all* supertypes *and* satisfy the characteristic predicate, if provided.
|
||||
|
||||
Member predicates are inherited and can be overridden.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class EmptyBlock extends Block {
|
||||
EmptyBlock() {
|
||||
this.getNumStmt() = 0
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
`Classes <https://help.semmle.com/QL/ql-handbook/types.html#classes>`__ model sets of values from the database. A class has one or more supertypes, and inherits `member predicates <https://help.semmle.com/QL/ql-handbook/types.html#member-predicates>`__ (methods) from each of them. Each value in a class must be in every supertype, but additional conditions can be stated in a so-called **characteristic predicate**, which looks a bit like a zero-argument constructor.
|
||||
|
||||
In the example, declaring a variable “EmptyBlock e” will allow it to range over only those blocks that have zero statements.
|
||||
|
||||
Classes in QL continued
|
||||
=======================
|
||||
|
||||
.. container:: column-left
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/empty-if-cpp-predicate.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 3-5
|
||||
|
||||
.. container:: column-right
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/empty-if-cpp-class.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 3-7
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
As shown in this example, classes behave much like unary predicates, but with ``instanceof`` instead of predicate calls to check membership. Later on, we will see how to define member predicates on classes.
|
||||
|
||||
Iterative query refinement
|
||||
==========================
|
||||
|
||||
- **Common workflow**: Start with a simple query, inspect a few results, refine, repeat.
|
||||
|
||||
- For example, empty then branches are not a problem if there is an else.
|
||||
|
||||
- **Exercise**: How can we refine the query to take this into account?
|
||||
|
||||
**Hints**:
|
||||
|
||||
- Use member predicate ``IfStmt.getElse()``
|
||||
- Use ``not exists(...)``
|
||||
|
||||
.. note::
|
||||
|
||||
CodeQL makes it very easy to experiment with analysis ideas. A common workflow is to start with a simple query (like our “redundant if-statement” example), examine a few results, refine the query based on any patterns that emerge and repeat.
|
||||
|
||||
As an exercise, refine the redundant-if query based on the observation that if the if-statement has an “else” clause, then even if the body of the “then” clause is empty, it’s not actually redundant.
|
||||
|
||||
Model answer: redundant if-statement
|
||||
=====================================
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/empty-if-cpp-model.ql
|
||||
|
||||
.. note::
|
||||
|
||||
You can explore the results generated when this query is run on exiv2 in LGTM `here <https://lgtm.com/query/4641433299746527262/>`__.
|
||||
120
docs/codeql/ql-training/cpp/program-representation-cpp.rst
Normal file
@@ -0,0 +1,120 @@
|
||||
======================
|
||||
Program representation
|
||||
======================
|
||||
|
||||
CodeQL for C/C++
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- Abstract syntax trees
|
||||
- Database representation
|
||||
- Symbol tables
|
||||
- Variables
|
||||
- Functions
|
||||
|
||||
.. insert abstract-syntax-tree.rst
|
||||
|
||||
.. include:: ../slide-snippets/abstract-syntax-tree.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
AST CodeQL classes
|
||||
==================
|
||||
|
||||
Important AST CodeQL classes include:
|
||||
|
||||
- ``Expr``: expressions such as assignments, variable references, function calls, ...
|
||||
- ``Stmt``: statements such as conditionals, loops, try statements, ...
|
||||
- ``DeclarationEntry``: places where functions, variables or types are declared and/or defined
|
||||
|
||||
These three (and all other AST CodeQL classes) are subclasses of ``Element``.
|
||||
|
||||
Symbol table
|
||||
============
|
||||
|
||||
The database also includes information about the symbol table associated with a program:
|
||||
|
||||
- ``Variable``: all variables, including local variables, global variables, static variables and member variables
|
||||
|
||||
- ``Function``: all functions, including member function
|
||||
|
||||
- ``Type``: built-in and user-defined types
|
||||
|
||||
Working with variables
|
||||
======================
|
||||
|
||||
``Variable`` represents program variables, including locally scoped variables (``LocalScopeVariable``), global variables (``GlobalVariable``), and others:
|
||||
|
||||
- ``string Variable.getName()``
|
||||
- ``Type Variable.getType()``
|
||||
|
||||
``Access`` represents references to declared entities such as functions (``FunctionAccess``) and variables (``VariableAccess``), including fields (``FieldAccess``).
|
||||
|
||||
- ``Declaration Access.getTarget()``
|
||||
|
||||
``VariableDeclarationEntry`` represents declarations or definitions of a variable.
|
||||
|
||||
- ``Variable VariableDeclarationEntry.getVariable()``
|
||||
|
||||
Working with functions
|
||||
======================
|
||||
|
||||
Functions are represented by the Function class. Each declaration or definition of a function is represented by a ``FunctionDeclarationEntry``.
|
||||
|
||||
Calls to functions are modeled by the CodeQL class ``Call`` and its subclasses:
|
||||
|
||||
- ``Call.getTarget()`` gets the declared target of the call; undefined for calls through function pointers
|
||||
- ``Function.getACallToThisFunction()`` gets a call to this function
|
||||
|
||||
Typically, functions are identified by name:
|
||||
|
||||
- ``string Function.getName()``
|
||||
- ``string Function.getQualifiedName()``
|
||||
|
||||
Working with preprocessor logic
|
||||
===============================
|
||||
|
||||
Macros and other preprocessor directives can easily cause confusion when analyzing programs:
|
||||
|
||||
- AST structure reflects the program *after* preprocessing.
|
||||
- Locations refer to the original source text *before* preprocessing.
|
||||
|
||||
For example, in:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#define square(x) x*x
|
||||
y = square(y0), z = square(z0)
|
||||
|
||||
there are no AST nodes corresponding to ``square(y0)`` or ``square(z0)``, but there are AST nodes corresponding to ``y0*y0`` and ``z0*z0``.
|
||||
|
||||
.. note::
|
||||
|
||||
The C preprocessor poses a dilemma: un-preprocessed code cannot, in general, be parsed and analyzed meaningfully, but showing results in preprocessed code is not useful to developers. Our solution is to base the AST representation on preprocessed source (in the same way as compilers do), but associate AST nodes with locations in the original source text.
|
||||
|
||||
Working with macros
|
||||
===================
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#define square(x) x*x
|
||||
y = square(y0), z = square(z0)
|
||||
|
||||
is represented in the CodeQL database as:
|
||||
|
||||
- A Macro entity representing the text of the *head* and *body* of the macro
|
||||
- Assignment nodes, representing the two assignments after preprocessing
|
||||
|
||||
- Left-hand sides are ``VariableAccess`` nodes of y and z
|
||||
- Right-hand sides are ``MulExpr`` nodes representing ``y0*y0`` and ``z0*z0``
|
||||
|
||||
- A ``MacroAccess`` entity, which associates the Macro with the ``MulExprs``
|
||||
|
||||
Useful predicates on ``Element``: ``isInMacroExpansion()``, ``isAffectedByMacro()``
|
||||
|
||||
.. note::
|
||||
|
||||
The CodeQL database also contains information about macro definitions, which are represented by class ``Macro``. These macro definitions are related to the AST nodes resulting from their uses by the class ``MacroAccess``.
|
||||
101
docs/codeql/ql-training/cpp/snprintf.rst
Normal file
@@ -0,0 +1,101 @@
|
||||
===============================
|
||||
Exercise: ``snprintf`` overflow
|
||||
===============================
|
||||
|
||||
CodeQL for C/C++
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `rsyslog database <https://downloads.lgtm.com/snapshots/cpp/rsyslog/rsyslog/rsyslog-all-revision-2018-April-27--14-12-31.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `rsyslog <https://github.com/rsyslog/rsyslog>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:1506087977050/lang:cpp/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
``snprintf``
|
||||
============
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
- ``printf``: Returns number of characters printed.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
printf("Hello %s!", name)
|
||||
|
||||
- ``sprintf``: Returns number of characters written to ``buf``.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
sprintf(buf, "Hello %s!", name)
|
||||
|
||||
- ``snprintf``: Returns number of characters it **would have written** to ``buf`` had ``n`` been sufficiently large, **not** the number of characters actually written.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
snprintf(buf, n, "Hello %s!", name)
|
||||
|
||||
- In pre-C99 versions of glibc ``snprintf`` would return -1 if ``n`` was too small!
|
||||
|
||||
RCE in rsyslog
|
||||
==============
|
||||
|
||||
- Vulnerable code looked similar to this (`original <https://github.com/rsyslog/librelp/blob/532aa362f0f7a8d037505b0a27a1df452f9bac9e/src/tcp.c#L1195-L1211>`__):
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
char buf[1024];
|
||||
int pos = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
pos += snprintf(buf + pos, sizeof(buf) - pos, "%s", strs[i]);
|
||||
}
|
||||
|
||||
- Disclosed as `CVE-2018-1000140 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000140>`__.
|
||||
- Blog post: https://securitylab.github.com/research/librelp-buffer-overflow-cve-2018-1000140
|
||||
|
||||
Finding the RCE yourself
|
||||
========================
|
||||
|
||||
#. Write a query to find calls to ``snprintf``
|
||||
|
||||
**Hint**: Use class ``FunctionCall``
|
||||
|
||||
#. Restrict to calls whose result is used
|
||||
|
||||
**Hint**: Use class ``ExprInVoidContext``
|
||||
|
||||
#. Restrict to calls where the format string contains “%s”
|
||||
|
||||
**Hint**: Use predicates ``Expr.getValue`` and ``string.regexpMatch``
|
||||
|
||||
#. Restrict to calls where the result flows back to the size argument
|
||||
|
||||
**Hint**: Import library ``semmle.code.cpp.dataflow.TaintTracking`` and use predicate ``TaintTracking::localTaint``
|
||||
|
||||
Model answer
|
||||
============
|
||||
|
||||
.. literalinclude:: ../query-examples/cpp/snprintf-1.ql
|
||||
:language: ql
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
- More full-featured version: `https://lgtm.com/rules/1505913226124 <https://lgtm.com/rules/1505913226124>`__.
|
||||
|
||||
.. note::
|
||||
|
||||
The regular expression for matching the format string uses the “(?s)” directive to ensure that “.” also matches any newline characters embedded in the string.
|
||||
10
docs/codeql/ql-training/index.rst
Normal file
@@ -0,0 +1,10 @@
|
||||
CodeQL training and variant analysis examples
|
||||
=============================================
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
|
||||
*
|
||||
*/**
|
||||
137
docs/codeql/ql-training/java/apache-struts-java.rst
Normal file
@@ -0,0 +1,137 @@
|
||||
=======================
|
||||
Exercise: Apache Struts
|
||||
=======================
|
||||
|
||||
.. container:: subheading
|
||||
|
||||
Unsafe deserialization leading to an RCE
|
||||
|
||||
CVE-2017-9805
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `Apache Struts database <https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-7fd1622-CVE-2018-11776.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `Apache Struts <https://github.com/apache/struts>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:1878521151/lang:java/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
Unsafe deserialization in Struts
|
||||
================================
|
||||
|
||||
Apache Struts provides a ``ContentTypeHandler`` interface, which can be implemented for specific content types. It defines the following interface method:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
void toObject(Reader in, Object target);
|
||||
|
||||
|
||||
which is intended to populate the ``target`` object with data from the reader, usually through deserialization. However, the ``in`` parameter should be considered untrusted, and should not be deserialized without sanitization.
|
||||
|
||||
RCE in Apache Struts
|
||||
====================
|
||||
|
||||
- Vulnerable code looked like this (`original <https://lgtm.com/projects/g/apache/struts/snapshot/b434c23f95e0f9d5bde789bfa07f8fc1d5a8951d/files/plugins/rest/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java?sort=name&dir=ASC&mode=heatmap#L45>`__):
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
public void toObject(Reader in, Object target) {
|
||||
XStream xstream = createXStream();
|
||||
xstream.fromXML(in, target);
|
||||
}
|
||||
|
||||
- Xstream allows deserialization of **dynamic proxies**, which permit remote code execution.
|
||||
|
||||
- Disclosed as `CVE-2017-9805 <http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9805>`__
|
||||
|
||||
- Blog post: https://securitylab.github.com/research/apache-struts-vulnerability-cve-2017-9805
|
||||
|
||||
Finding the RCE yourself
|
||||
========================
|
||||
|
||||
#. Create a class to find the interface ``org.apache.struts2.rest.handler.ContentTypeHandler``
|
||||
|
||||
**Hint**: Use predicate ``hasQualifiedName(...)``
|
||||
|
||||
#. Identify methods called ``toObject``, which are defined on direct subtypes of ``ContentTypeHandler``
|
||||
|
||||
**Hint**: Use ``Method.getDeclaringType()`` and ``Type.getASupertype()``
|
||||
|
||||
#. Implement a ``DataFlow::Configuration``, defining the source as the first parameter of a ``toObject`` method, and the sink as an instance of ``UnsafeDeserializationSink``.
|
||||
|
||||
**Hint**: Use ``Node::asParameter()``
|
||||
|
||||
#. Construct the query as a path-problem query, and verify you find one result.
|
||||
|
||||
Model answer, step 1
|
||||
====================
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import java
|
||||
|
||||
/** The interface `org.apache.struts2.rest.handler.ContentTypeHandler`. */
|
||||
|
||||
class ContentTypeHandler extends RefType {
|
||||
ContentTypeHandler() {
|
||||
this.hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")
|
||||
}
|
||||
}
|
||||
|
||||
Model answer, step 2
|
||||
====================
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
/** A `toObject` method on a subtype of `org.apache.struts2.rest.handler.ContentTypeHandler`. */
|
||||
class ContentTypeHandlerDeserialization extends Method {
|
||||
ContentTypeHandlerDeserialization() {
|
||||
this.getDeclaringType().getASupertype() instanceof ContentTypeHandler and
|
||||
this.hasName("toObject")
|
||||
|
||||
Model answer, step 3
|
||||
====================
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import UnsafeDeserialization
|
||||
import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
/**
|
||||
* Configuration that tracks the flow of taint from the first parameter of
|
||||
* `ContentTypeHandler.toObject` to an instance of unsafe deserialization.
|
||||
*/
|
||||
class StrutsUnsafeDeserializationConfig extends Configuration {
|
||||
StrutsUnsafeDeserializationConfig() { this = "StrutsUnsafeDeserializationConfig" }
|
||||
override predicate isSource(Node source) {
|
||||
source.asParameter() = any(ContentTypeHandlerDeserialization des).getParameter(0)
|
||||
}
|
||||
override predicate isSink(Node sink) { sink instanceof UnsafeDeserializationSink }
|
||||
}
|
||||
|
||||
Model answer, step 4
|
||||
====================
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import PathGraph
|
||||
...
|
||||
from PathNode source, PathNode sink, StrutsUnsafeDeserializationConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
and sink.getNode() instanceof UnsafeDeserializationSink
|
||||
select sink.getNode().(UnsafeDeserializationSink).getMethodAccess(), source, sink, "Unsafe deserialization of $@.", source, "user input"
|
||||
|
||||
More full-featured version: https://github.com/github/security-lab/tree/main/CodeQL_Queries/java/Apache_Struts_CVE-2017-9805
|
||||
142
docs/codeql/ql-training/java/data-flow-java.rst
Normal file
@@ -0,0 +1,142 @@
|
||||
=========================
|
||||
Introduction to data flow
|
||||
=========================
|
||||
|
||||
Finding SPARQL injection vulnerabilities in Java
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `VIVO Vitro database <http://downloads.lgtm.com/snapshots/java/vivo-project/Vitro/vivo-project_Vitro_java-srcVersion_47ae42c01954432c3c3b92d5d163551ce367f510-dist_odasa-lgtm-2019-04-23-7ceff95-linux64.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `VIVO Vitro <https://github.com/vivo-project/Vitro>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:14040005/lang:java/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- SPARQL injection
|
||||
- Data flow
|
||||
- Modules and libraries
|
||||
- Local data flow
|
||||
- Local taint tracking
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
`SPARQL <https://en.wikipedia.org/wiki/SPARQL>`__ is a language for querying key-value databases in RDF format, which can suffer from SQL injection-like vulnerabilities:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
sparqlAskQuery("ASK { <" + individualURI + "> ?p ?o }")
|
||||
|
||||
``individualURI`` is provided by a user, allowing an attacker to prematurely close the ``>``, and provide additional content.
|
||||
|
||||
**Goal**: Find query strings that are created by concatenation.
|
||||
|
||||
.. note::
|
||||
|
||||
If you have completed the “Example: Query injection” slide deck which was part of the previous course, this example will look familiar to you.
|
||||
|
||||
To understand the scope of this vulnerability, consider what would happen if a malicious user could provide the following as the content of the ``individualURI`` variable:
|
||||
|
||||
``“http://vivoweb.org/ontology/core#FacultyMember> ?p ?o . FILTER regex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!", "(.*a){50}") } #``
|
||||
|
||||
|
||||
Example: SPARQL injection
|
||||
=========================
|
||||
|
||||
We can write a simple query that finds string concatenations that occur in calls to SPARQL query APIs.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/java/data-flow-java-1.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
This is similar, but not identical, to the formulation we had in the previous training deck. It has been rewritten to make it easier for the next step.
|
||||
|
||||
Success! But also missing results...
|
||||
====================================
|
||||
|
||||
Query finds a CVE reported by Semmle (CVE-2019-6986), plus one other result, but misses other opportunities where:
|
||||
|
||||
- String concatenation occurs on a different line in the same method.
|
||||
- String concatenation occurs in a different method.
|
||||
- String concatenation occurs through ``StringBuilders`` or similar.
|
||||
- Entirety of user input is provided as the query.
|
||||
|
||||
We want to improve our query to catch more of these cases.
|
||||
|
||||
.. note::
|
||||
|
||||
For more details of the CVE, see: https://github.com/Semmle/SecurityExploits/tree/master/vivo-project/CVE-2019-6986
|
||||
|
||||
As an example, consider this SPARQL query call:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
String queryString = "ASK { <" + individualURI + "> ?p ?o }";
|
||||
sparqlAskQuery(queryString);
|
||||
|
||||
Here the concatenation occurs before the call, so the existing query would miss this - the string concatenation does not occur *directly* as the first argument of the call.
|
||||
|
||||
.. include general data flow slides
|
||||
|
||||
.. include:: ../slide-snippets/local-data-flow.rst
|
||||
|
||||
.. resume language-specific slides
|
||||
|
||||
Exercise: revisiting SPARQL injection
|
||||
=====================================
|
||||
|
||||
Refine the query to find string concatenation that occurs in the same method, but a different line.
|
||||
|
||||
**Hint**: Use ``DataFlow::localFlow`` to assert that the result flows to the SPARQL call argument, using ``DataFlow::exprNode`` to get the data flow nodes for the relevant expression nodes.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/java/data-flow-java-2.ql
|
||||
:language: ql
|
||||
|
||||
Refinements (take home exercise)
|
||||
================================
|
||||
|
||||
In Java, strings are often created using ``StringBuilder`` and ``StringBuffer`` classes. For example:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
queryBuilder.add("ASK { <");
|
||||
queryBuilder.add(individualURI);
|
||||
queryBuilder.add("> ?p ?o }");
|
||||
sparqlAskQuery(queryBuilder);
|
||||
|
||||
**Exercise**: Refine the query to consider strings created from ``StringBuilder`` and ``StringBuffer`` classes as sources of concatenation.
|
||||
|
||||
Beyond local data flow
|
||||
======================
|
||||
|
||||
- We are still missing possible results.
|
||||
|
||||
- Concatenation that occurs outside the enclosing method.
|
||||
|
||||
- Instead, let’s turn the problem around and find user-controlled data that flows into a ``printf`` format argument, potentially through calls.
|
||||
- This needs :doc:`global data flow <global-data-flow-java>`.
|
||||
186
docs/codeql/ql-training/java/global-data-flow-java.rst
Normal file
@@ -0,0 +1,186 @@
|
||||
================================
|
||||
Introduction to global data flow
|
||||
================================
|
||||
|
||||
CodeQL for Java
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `Apache Struts database <https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-7fd1622-CVE-2018-11776.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `Apache Struts <https://github.com/apache/struts>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:1878521151/lang:java/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- Global taint tracking
|
||||
- Sanitizers
|
||||
- Path queries
|
||||
- Data flow models
|
||||
|
||||
.. insert common global data flow slides
|
||||
|
||||
.. include:: ../slide-snippets/global-data-flow.rst
|
||||
|
||||
.. resume language-specific global data flow slides
|
||||
|
||||
Code injection in Apache struts
|
||||
===============================
|
||||
|
||||
- In April 2018, Man Yue Mo, a security researcher at Semmle, reported 5 remote code execution (RCE) vulnerabilities (CVE-2018-11776) in Apache Struts.
|
||||
|
||||
- These vulnerabilities were caused by untrusted, unsanitized data being evaluated as an OGNL (Object Graph Navigation Library) expression, allowing malicious users to perform remote code execution.
|
||||
|
||||
- Conceptually, this is a global taint tracking problem - does untrusted remote input flow to a method call which evaluates OGNL?
|
||||
|
||||
.. note::
|
||||
|
||||
More details on the CVE can be found here: https://securitylab.github.com/research/apache-struts-CVE-2018-11776 and
|
||||
https://github.com/github/security-lab/tree/main/CodeQL_Queries/java/Apache_Struts_CVE-2018-11776
|
||||
|
||||
More details on OGNL can be found here: https://commons.apache.org/proper/commons-ognl/
|
||||
|
||||
.. rst-class:: java-data-flow-code-example
|
||||
|
||||
Code example
|
||||
============
|
||||
|
||||
Finding RCEs (outline)
|
||||
======================
|
||||
|
||||
.. literalinclude:: ../query-examples/java/global-data-flow-java-1.ql
|
||||
:language: ql
|
||||
|
||||
Defining sources
|
||||
================
|
||||
|
||||
We want to look for method calls where the method name is ``getNamespace()``, and the declaring type of the method is a class called ``ActionProxy``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import semmle.code.java.security.Security
|
||||
|
||||
class TaintedOGNLConfig extends TaintTracking::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Method m |
|
||||
m.getName() = "getNamespace" and
|
||||
m.getDeclaringType().getName() = "ActionProxy" and
|
||||
source.asExpr() = m.getAReference()
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
We first define what it means to be a *source* of tainted data for this particular problem. In this case, we are interested in the value returned by calls to ``getNamespace()``.
|
||||
|
||||
|
||||
Exercise: Defining sinks
|
||||
========================
|
||||
|
||||
Fill in the definition of ``isSink``.
|
||||
|
||||
**Hint**: We want to find the first argument of calls to the method ``compileAndExecute``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import semmle.code.java.security.Security
|
||||
|
||||
class TaintedOGNLConfig extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
/* Fill me in */
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
The second part is to define what it means to be a sink for this particular problem. The queries from an :doc:`Introduction to data flow <data-flow-java>` will be useful for this exercise.
|
||||
|
||||
Solution: Defining sinks
|
||||
========================
|
||||
|
||||
Find a method access to ``compileAndExecute``, and mark the first argument.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import semmle.code.java.security.Security
|
||||
|
||||
class TaintedOGNLConfig extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getName() = "compileAndExecute" and
|
||||
ma.getArgument(0) = sink.asExpr()
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. insert path queries slides
|
||||
|
||||
.. include:: ../slide-snippets/path-queries.rst
|
||||
|
||||
.. resume language-specific global data flow slides
|
||||
|
||||
Defining sanitizers
|
||||
===================
|
||||
|
||||
A sanitizer allows us to *prevent* flow through a particular node in the graph. For example, flows that go via ``ValueStackShadowMap`` are not particularly interesting, because it is a class that is rarely used in practice. We can exclude them like so:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TaintedOGNLConfig extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node nd) {
|
||||
nd.getEnclosingCallable()
|
||||
.getDeclaringType()
|
||||
.getName() = "ValueStackShadowMap"
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
Defining additional taint steps
|
||||
===============================
|
||||
|
||||
Add an additional taint step that (heuristically) taints a local variable if it is a pointer, and it is passed to a function in a parameter position that taints it.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TaintedOGNLConfig extends TaintTracking::Configuration {
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1,
|
||||
DataFlow::Node node2) {
|
||||
exists(Field f, RefType t |
|
||||
node1.asExpr() = f.getAnAssignedValue() and
|
||||
node2.asExpr() = f.getAnAccess() and
|
||||
node1.asExpr().getEnclosingCallable().getDeclaringType() = t and
|
||||
node2.asExpr().getEnclosingCallable().getDeclaringType() = t
|
||||
)
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
.. rst-class:: end-slide
|
||||
|
||||
Extra slides
|
||||
============
|
||||
|
||||
.. include:: ../slide-snippets/global-data-flow-extra-slides.rst
|
||||
207
docs/codeql/ql-training/java/intro-ql-java.rst
Normal file
@@ -0,0 +1,207 @@
|
||||
================================
|
||||
Introduction to variant analysis
|
||||
================================
|
||||
|
||||
CodeQL for Java
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `Apache Struts database <https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-7fd1622-CVE-2018-11776.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `Apache Struts <https://github.com/apache/struts>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:1878521151/lang:java/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
.. Include language-agnostic section here
|
||||
|
||||
.. include:: ../slide-snippets/intro-ql-general.rst
|
||||
|
||||
Oops
|
||||
====
|
||||
|
||||
.. code-block:: java
|
||||
:emphasize-lines: 3
|
||||
|
||||
int write(int[] buf, int size, int loc, int val) {
|
||||
if (loc >= size) {
|
||||
// return -1;
|
||||
}
|
||||
|
||||
buf[loc] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- The return statement has been commented out (during debugging?)
|
||||
- The ``if`` statement is now dead code
|
||||
- No explicit bounds checking, will throw ``ArrayIndexOutOfbounds``
|
||||
|
||||
.. note::
|
||||
|
||||
Here’s a simple (artificial) bug, which we’ll develop a query to catch.
|
||||
|
||||
This function writes a value to a given location in an array, first trying to do a bounds check to validate that the location is within bounds. However, the return statement has been commented out, leaving a redundant if statement and no bounds checking.
|
||||
|
||||
This case can act as our “patient zero” in the variant analysis game.
|
||||
|
||||
A simple CodeQL query
|
||||
=====================
|
||||
|
||||
.. literalinclude:: ../query-examples/java/empty-if-java.ql
|
||||
:language: ql
|
||||
|
||||
.. note::
|
||||
|
||||
We are going to write a simple query which finds “if statements” with empty “then” blocks, so we can highlight the results like those on the previous slide. The query can be run in the `query console on LGTM <https://lgtm.com/query>`__, or in your `IDE <https://lgtm.com/help/lgtm/running-queries-ide>`__.
|
||||
|
||||
A `query <https://help.semmle.com/QL/ql-handbook/queries.html>`__ consists of a “select” clause that indicates what results should be returned. Typically it will also provide a “from” clause to declare some variables, and a “where” clause to state conditions over those variables. For more information on the structure of query files (including links to useful topics in the `QL language reference <https://help.semmle.com/QL/ql-handbook/index.html>`__), see `About CodeQL queries <https://help.semmle.com/QL/learn-ql/ql/writing-queries/introduction-to-queries.html>`__.
|
||||
|
||||
In our example here, the first line of the query imports the `CodeQL library for Java <https://help.semmle.com/qldoc/java/>`__, which defines concepts like ``IfStmt`` and ``Block``.
|
||||
The query proper starts by declaring two variables–ifStmt and block. These variables represent sets of values in the database, according to the type of each of the variables. For example, ``ifStmt`` has the type ``IfStmt``, which means it represents the set of all if statements in the program.
|
||||
|
||||
If we simply selected these two variables::
|
||||
|
||||
from IfStmt ifStmt, Block block
|
||||
select ifStmt, block
|
||||
|
||||
We would get a result row for every combination of blocks and if statements in the program. This is known as a cross-product, because there is no logical condition linking the two variables. We can use the where clause to specify the condition that we are only interested in rows where the “block” is the “then” part of the if statement. We do this by specifying::
|
||||
|
||||
block = ifStmt.getThen()
|
||||
|
||||
This states that the block is equal to (not assigned!) the “then” part of the ``ifStmt``. ``getThen()`` is an operation which is available on any IfStmt. One way to interpret this is as a filtering operation – starting with every pair of block and if statements, check each one to whether the logical condition holds, and only keep the row if that is the case.
|
||||
We can add a second condition that specifies the block must be empty::
|
||||
|
||||
and block.isEmpty()
|
||||
|
||||
The ``isEmpty()`` operation is available on any Block, and is only true if the “block” has no children.
|
||||
|
||||
Finally, we select a location, at which to report the problem, and a message, to explain what the problem is.
|
||||
|
||||
|
||||
Structure of a query
|
||||
=====================
|
||||
|
||||
A **query file** has the extension ``.ql`` and contains a **query clause**, and optionally **predicates**, **classes**, and **modules**.
|
||||
|
||||
A **query library** has the extension ``.qll`` and does not contain a query clause, but may contain modules, classes, and predicates.
|
||||
|
||||
Each query library also implicitly defines a module.
|
||||
|
||||
**Import** statements allow the classes and predicates defined in one module to be used in another.
|
||||
|
||||
.. note::
|
||||
|
||||
Queries are always contained in query files with the file extension ``.ql``.
|
||||
|
||||
Parts of queries can be lifted into `library files <https://help.semmle.com/QL/ql-handbook/modules.html#library-modules>`__ with the extension ``.qll``. Definitions within such libraries can be brought into scope using “import” statements, and similarly QLL files can import each other’s definitions using “import” statements.
|
||||
|
||||
Logic can be encapsulated as user-defined `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ and `classes <https://help.semmle.com/QL/ql-handbook/types.html#classes>`__, and organized into `modules <https://help.semmle.com/QL/ql-handbook/modules.html>`__. Each QLL file implicitly defines a module, but QL and QLL files can also contain explicit module definitions, as we will see later.
|
||||
|
||||
Predicates
|
||||
==========
|
||||
|
||||
A predicate allows you to pull out and name parts of a query.
|
||||
|
||||
.. container:: column-left
|
||||
|
||||
.. literalinclude:: ../query-examples/java/empty-if-java.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 6
|
||||
|
||||
.. container:: column-right
|
||||
|
||||
.. literalinclude:: ../query-examples/java/empty-if-java-predicate.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 3-5
|
||||
|
||||
.. note::
|
||||
|
||||
A `predicate <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ takes zero or more parameters, and its body is a condition on those parameters. The predicate may (or may not) hold. Predicates may also be `recursive <https://help.semmle.com/QL/ql-handbook/predicates.html#recursive-predicates>`__, simply by referring to themselves (directly or indirectly).
|
||||
|
||||
You can imagine a predicate to be a self-contained from-where-select statement, that produces an intermediate relation, or table. In this case, the ``isEmpty`` predicate will be the set of all blocks which are empty.
|
||||
|
||||
|
||||
Classes in QL
|
||||
=============
|
||||
|
||||
A QL class allows you to name a set of values and define (member) predicates on them.
|
||||
|
||||
A class has at least one supertype and optionally a **characteristic predicate**; it contains the values that belong to *all* supertypes *and* satisfy the characteristic predicate, if provided.
|
||||
|
||||
Member predicates are inherited and can be overridden.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class EmptyBlock extends Block {
|
||||
EmptyBlock() {
|
||||
this.getNumStmt() = 0
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
`Classes <https://help.semmle.com/QL/ql-handbook/types.html#classes>`__ model sets of values from the database. A class has one or more supertypes, and inherits `member predicates <https://help.semmle.com/QL/ql-handbook/types.html#member-predicates>`__ (methods) from each of them. Each value in a class must be in every supertype, but additional conditions can be stated in a so-called **characteristic predicate**, which looks a bit like a zero-argument constructor.
|
||||
|
||||
In the example, declaring a variable “EmptyBlock e” will allow it to range over only those blocks that have zero statements.
|
||||
|
||||
Classes in QL continued
|
||||
=======================
|
||||
|
||||
.. container:: column-left
|
||||
|
||||
.. literalinclude:: ../query-examples/java/empty-if-java-predicate.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 3-5
|
||||
|
||||
.. container:: column-right
|
||||
|
||||
.. literalinclude:: ../query-examples/java/empty-if-java-class.ql
|
||||
:language: ql
|
||||
:emphasize-lines: 3-7
|
||||
|
||||
.. note::
|
||||
|
||||
As shown in this example, classes behave much like unary predicates, but with ``instanceof`` instead of predicate calls to check membership. Later on, we will see how to define member predicates on classes.
|
||||
|
||||
Iterative query refinement
|
||||
==========================
|
||||
|
||||
- **Common workflow**: Start with a simple query, inspect a few results, refine, repeat.
|
||||
|
||||
- For example, empty ``then`` branches are not a problem if there is an ``else``.
|
||||
|
||||
- **Exercise**: How can we refine the query to take this into account?
|
||||
|
||||
**Hints**:
|
||||
|
||||
- Use member predicate ``IfStmt.getElse()``
|
||||
- Use ``not exists(...)``
|
||||
|
||||
.. note::
|
||||
|
||||
CodeQL makes it very easy to experiment with analysis ideas. A common workflow is to start with a simple query (like our “redundant if-statement” example), examine a few results, refine the query based on any patterns that emerge and repeat.
|
||||
|
||||
As an exercise, refine the redundant-if query based on the observation that if the if-statement has an “else” clause, then even if the body of the “then” clause is empty, it’s not actually redundant.
|
||||
|
||||
Model answer: redundant if-statement
|
||||
====================================
|
||||
|
||||
.. literalinclude:: ../query-examples/java/empty-if-java-model.ql
|
||||
|
||||
.. note::
|
||||
|
||||
You can explore the results generated when this query is run on apache/struts in LGTM `here <https://lgtm.com/query/1269550358355690774/>`__.
|
||||
94
docs/codeql/ql-training/java/program-representation-java.rst
Normal file
@@ -0,0 +1,94 @@
|
||||
======================
|
||||
Program representation
|
||||
======================
|
||||
|
||||
CodeQL for Java
|
||||
|
||||
.. rst-class:: agenda
|
||||
|
||||
Agenda
|
||||
======
|
||||
|
||||
- Abstract syntax trees
|
||||
- Database representation
|
||||
- Program elements
|
||||
- AST CodeQL classes
|
||||
|
||||
.. insert abstract-syntax-tree.rst
|
||||
|
||||
.. include:: ../slide-snippets/abstract-syntax-tree.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
Program elements
|
||||
================
|
||||
|
||||
- The CodeQL class ``Element`` represents program elements with a name.
|
||||
- This includes: packages (``Package``), compilation units (``CompilationUnit``), types (``Type``), methods (``Method``), constructors (``Constructor``), and variables (``Variable``).
|
||||
- It is often convenient to refer to an element that might either be a method or a constructor; the class ``Callable``, which is a common superclass of ``Method`` and ``Constructor``, can be used for this purpose.
|
||||
|
||||
|
||||
AST
|
||||
===
|
||||
|
||||
There are two primary AST CodeQL classes, used within ``Callables``:
|
||||
|
||||
- ``Expr``: expressions such as assignments, variable references, function calls, ...
|
||||
- ``Stmt``: statements such as conditionals, loops, try statements, ...
|
||||
|
||||
Operations are provided for exploring the AST:
|
||||
|
||||
- ``Expr.getAChildExpr`` returns a sub-expression of a given expression.
|
||||
- ``Stmt.getAChild`` returns a statement or expression that is nested directly inside a given statement.
|
||||
- ``Expr.getParent`` and ``Stmt.getParent`` return the parent node of an AST node.
|
||||
|
||||
Types
|
||||
=====
|
||||
|
||||
The database also includes information about the types used in a program:
|
||||
|
||||
- ``PrimitiveType`` represents a `primitive type <http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html>`__, that is, one of ``boolean``, ``byte``, ``char``, ``double``, ``float``, ``int``, ``long``, ``short``. CodeQL also classifies ``void`` and ``<nulltype>`` (the type of the ``null`` literal) as primitive types.
|
||||
- ``RefType`` represents a reference type; it has several subclasses:
|
||||
|
||||
- ``Class`` represents a Java class.
|
||||
- ``Interface`` represents a Java interface.
|
||||
- ``EnumType`` represents a Java enum type.
|
||||
- ``Array`` represents a Java array type.
|
||||
|
||||
Working with variables
|
||||
======================
|
||||
|
||||
``Variable`` represents program variables, including locally scoped variables (``LocalScopeVariable``), fields (``Fields``), and parameters (``Parameters``):
|
||||
|
||||
- ``string Variable.getName()``
|
||||
- ``Type Variable.getType()``
|
||||
|
||||
``Access`` represents references to declared entities such as methods (``MethodAccess``) and variables (``VariableAccess``), including fields (``FieldAccess``).
|
||||
|
||||
- ``Declaration Access.getTarget()``
|
||||
|
||||
``VariableDeclarationEntry`` represents declarations or definitions of a variable.
|
||||
|
||||
- ``Variable VariableDeclarationEntry.getVariable()``
|
||||
|
||||
Working with callables
|
||||
======================
|
||||
|
||||
Callables are represented by the ``Callable`` CodeQL class.
|
||||
|
||||
Calls to callables are modeled by the CodeQL class ``Call`` and its subclasses:
|
||||
|
||||
- ``Call.getCallee()`` gets the declared target of the call
|
||||
- ``Call.getAReference()`` gets a call to this function
|
||||
|
||||
Typically, callables are identified by name:
|
||||
|
||||
- ``string Callable.getName()``
|
||||
- ``string Callable.getQualifiedName()``
|
||||
|
||||
.. rst-class:: java-expression-ast
|
||||
|
||||
Example: Java expression AST
|
||||
============================
|
||||
|
||||
.. diagram copied from google slides
|
||||
144
docs/codeql/ql-training/java/query-injection-java.rst
Normal file
@@ -0,0 +1,144 @@
|
||||
========================
|
||||
Example: Query injection
|
||||
========================
|
||||
|
||||
CodeQL for Java
|
||||
|
||||
.. rst-class:: setup
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
For this example you should download:
|
||||
|
||||
- `CodeQL for Visual Studio Code <https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html>`__
|
||||
- `VIVO Vitro database <http://downloads.lgtm.com/snapshots/java/vivo-project/Vitro/vivo-project_Vitro_java-srcVersion_47ae42c01954432c3c3b92d5d163551ce367f510-dist_odasa-lgtm-2019-04-23-7ceff95-linux64.zip>`__
|
||||
|
||||
.. note::
|
||||
|
||||
For this example, we will be analyzing `VIVO Vitro <https://github.com/vivo-project/Vitro>`__.
|
||||
|
||||
You can also query the project in `the query console <https://lgtm.com/query/project:14040005/lang:java/>`__ on LGTM.com.
|
||||
|
||||
.. insert database-note.rst to explain differences between database available to download and the version available in the query console.
|
||||
|
||||
.. include:: ../slide-snippets/database-note.rst
|
||||
|
||||
.. resume slides
|
||||
|
||||
SQL injection
|
||||
=============
|
||||
|
||||
- Occurs when user input is used to construct an SQL query without any sanitization or escaping.
|
||||
|
||||
- Classic example involves constructing a query using string concatenation:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
runQuery("SELECT * FROM users WHERE id='" + userId + "'");
|
||||
|
||||
|
||||
- If the ``userId`` can be provided by a user, and is not sanitized, then a malicious user can provide input that manipulates the intended query.
|
||||
|
||||
- For example, providing the input ``"' OR '1'='1"`` would allow the attacker to return all records in the users table.
|
||||
|
||||
.. note::
|
||||
|
||||
`SQL <https://en.wikipedia.org/wiki/SQL>`__ is a database query language, which is often used from within other programming languages to interact with a database. The typical case is that a query is to be executed to find some data, based on some input provided by the user - for example, the user’s ID. However, the interface between the host programming language and SQL is typically implemented by passing a string containing the query to some API.
|
||||
|
||||
SPARQL injection
|
||||
================
|
||||
|
||||
- `SPARQL <https://en.wikipedia.org/wiki/SPARQL>`__ is a language for querying key-value databases in RDF format.
|
||||
|
||||
- The same type of vulnerability can occur for SPARQL as for SQL: if the SPARQL query is constructed through string concatenation, a malicious user can subvert the query:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
sparqlAskQuery("ASK { <" + individualURI + "> ?p ?o }");
|
||||
|
||||
- SPARQL is used by many projects, but we will be looking at `VIVO Vitro <https://github.com/vivo-project/Vitro/>`__.
|
||||
|
||||
.. rst-class:: background2
|
||||
|
||||
Developing a query
|
||||
===================
|
||||
|
||||
Finding a query concatenation
|
||||
|
||||
CodeQL query: find SPARQL methods
|
||||
=================================
|
||||
|
||||
Let’s start by looking for calls to methods with names of the form ``sparql*Query``, using the classes ``Method`` and ``MethodAccess`` from the Java library.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/java/query-injection-java-1.ql
|
||||
|
||||
.. note::
|
||||
|
||||
- When performing variant analysis, it is usually helpful to write a simple query that finds the simple syntactic pattern, before trying to go on to describe the cases where it goes wrong.
|
||||
- In this case, we start by looking for all the method calls that appear to run, before trying to refine the query to find cases which are vulnerable to query injection.
|
||||
- The ``select`` clause defines what this query is looking for:
|
||||
|
||||
- a ``MethodAccess``: the call to a SPARQL query method
|
||||
- a ``Method``: the SPARQL query method.
|
||||
|
||||
- The ``where`` part of the query ties these variables together using `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ defined in the `standard CodeQL library for Java <https://help.semmle.com/qldoc/java/>`__.
|
||||
|
||||
CodeQL query: find string concatenation
|
||||
=======================================
|
||||
|
||||
- We now need to define what would make these API calls unsafe.
|
||||
- A simple heuristic would be to look for string concatenation used in the query argument.
|
||||
- We may want to reuse this logic, so let us create a separate predicate.
|
||||
|
||||
Looking at autocomplete suggestions, we see that we can get the type of an expression using the ``getType()`` method.
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isStringConcat(AddExpr ae) {
|
||||
ae.getType() instanceof TypeString
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
- An important part of the query is to determine whether a given expression is string concatenation.
|
||||
- We therefore write a helper predicate for finding string concatenation.
|
||||
- This predicate effectively represents the set of all ``add`` expressions in the database where the type of the expression is ``TypeString`` - that is, the addition produces a ``String`` value.
|
||||
|
||||
CodeQL query: SPARQL injection
|
||||
==============================
|
||||
|
||||
We can now combine our predicate with the existing query.
|
||||
Note that we do not need to specify that the argument of the method access is an ``AddExpr`` - this is implied by the ``isStringConcat`` requirement.
|
||||
|
||||
Now our query becomes:
|
||||
|
||||
.. rst-class:: build
|
||||
|
||||
.. literalinclude:: ../query-examples/java/query-injection-java-2.ql
|
||||
:language: ql
|
||||
|
||||
The final query
|
||||
===============
|
||||
|
||||
.. literalinclude:: ../query-examples/java/query-injection-java-3.ql
|
||||
:language: ql
|
||||
|
||||
There are two results, one of which was assigned **CVE-2019-6986**.
|
||||
|
||||
.. note::
|
||||
|
||||
Full write up and exploit can be found here: https://github.com/Semmle/SecurityExploits/tree/master/vivo-project/CVE-2019-6986
|
||||
|
||||
Follow up
|
||||
=========
|
||||
|
||||
- Our query successfully finds cases where the concatenation occurs in the argument to the SPARQL API.
|
||||
|
||||
- However, in general, the concatenation could occur before the method call.
|
||||
|
||||
- For this, we would need to use :doc:`local data flow <data-flow-java>`, which is the topic of the next set of training slides.
|
||||
@@ -0,0 +1,8 @@
|
||||
import cpp
|
||||
|
||||
from AddExpr a, Variable v, RelationalOperation cmp
|
||||
where
|
||||
a.getAnOperand() = v.getAnAccess() and
|
||||
cmp.getAnOperand() = a and
|
||||
cmp.getAnOperand() = v.getAnAccess()
|
||||
select cmp, "Overflow check."
|
||||
@@ -0,0 +1,9 @@
|
||||
import cpp
|
||||
|
||||
from AddExpr a, Variable v, RelationalOperation cmp
|
||||
where
|
||||
a.getAnOperand() = v.getAnAccess() and
|
||||
cmp.getAnOperand() = a and
|
||||
cmp.getAnOperand() = v.getAnAccess() and
|
||||
forall(Expr op | op = a.getAnOperand() | isSmall(op))
|
||||
select cmp, "Bad overflow check."
|
||||
@@ -0,0 +1,12 @@
|
||||
import cpp
|
||||
|
||||
predicate isSmall(Expr e) { e.getType().getSize() < 4 }
|
||||
|
||||
from AddExpr a, Variable v, RelationalOperation cmp
|
||||
where
|
||||
a.getAnOperand() = v.getAnAccess() and
|
||||
cmp.getAnOperand() = a and
|
||||
cmp.getAnOperand() = v.getAnAccess() and
|
||||
forall(Expr op | op = a.getAnOperand() | isSmall(op)) and
|
||||
not isSmall(a.getExplicitlyConverted())
|
||||
select cmp, "Bad overflow check"
|
||||
@@ -0,0 +1,8 @@
|
||||
import cpp
|
||||
|
||||
from FunctionCall alloc, FunctionCall free, LocalScopeVariable v
|
||||
where allocationCall(alloc)
|
||||
and alloc = v.getAnAssignedValue()
|
||||
and freeCall(free, v.getAnAccess())
|
||||
and alloc.getASuccessor+() = free
|
||||
select alloc, free
|
||||
@@ -0,0 +1,8 @@
|
||||
import cpp
|
||||
|
||||
from FunctionCall free, LocalScopeVariable v, VariableAccess u
|
||||
where freeCall(free, v.getAnAccess())
|
||||
and u = v.getAnAccess()
|
||||
and u.isRValue()
|
||||
and free.getASuccessor+() = u
|
||||
select free, u
|
||||
@@ -0,0 +1,10 @@
|
||||
import cpp
|
||||
|
||||
from LocalVariable lv, ControlFlowNode def
|
||||
where
|
||||
def = lv.getAnAssignment() and
|
||||
not exists(VariableAccess use |
|
||||
use = lv.getAnAccess() and
|
||||
use = def.getASuccessor+()
|
||||
)
|
||||
select lv, def
|
||||
@@ -0,0 +1,10 @@
|
||||
import cpp
|
||||
|
||||
predicate isReachable(BasicBlock bb) {
|
||||
bb instanceof EntryBasicBlock or
|
||||
isReachable(bb.getAPredecessor())
|
||||
}
|
||||
|
||||
from BasicBlock bb
|
||||
where not isReachable(bb)
|
||||
select bb
|
||||
@@ -0,0 +1,10 @@
|
||||
import cpp
|
||||
|
||||
from ExprCall c, PointerDereferenceExpr deref, VariableAccess va,
|
||||
Access fnacc
|
||||
where c.getLocation().getFile().getBaseName() = "cjpeg.c" and
|
||||
c.getLocation().getStartLine() = 640 and
|
||||
deref = c.getExpr() and
|
||||
va = deref.getOperand() and
|
||||
fnacc = va.getTarget().getAnAssignedValue()
|
||||
select c, fnacc.getTarget()
|
||||
@@ -0,0 +1,8 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Printf
|
||||
|
||||
from Call c, FormattingFunction ff, Expr format
|
||||
where c.getTarget() = ff and
|
||||
format = c.getArgument(ff.getFormatParameterIndex()) and
|
||||
not format instanceof StringLiteral
|
||||
select format, "Non-constant format string."
|
||||
@@ -0,0 +1,12 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.commons.Printf
|
||||
|
||||
class SourceNode extends DataFlow::Node { /* ... */ }
|
||||
|
||||
from FormattingFunction f, Call c, SourceNode src, DataFlow::Node arg
|
||||
where c.getTarget() = f and
|
||||
arg.asExpr() = c.getArgument(f.getFormatParameterIndex()) and
|
||||
DataFlow::localFlow(src, arg) and
|
||||
not src.asExpr() instanceof StringLiteral
|
||||
select arg, "Non-constant format string."
|
||||
@@ -0,0 +1,11 @@
|
||||
import cpp
|
||||
|
||||
class EmptyBlock extends Block {
|
||||
EmptyBlock() {
|
||||
this.isEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
from IfStmt ifStmt
|
||||
where ifstmt.getThen() instanceof EmptyBlock
|
||||
select ifstmt
|
||||
@@ -0,0 +1,11 @@
|
||||
import cpp
|
||||
|
||||
class EmptyBlock extends Block {
|
||||
EmptyBlock() { this.isEmpty() }
|
||||
}
|
||||
|
||||
from IfStmt ifstmt
|
||||
where
|
||||
ifstmt.getThen() instanceof EmptyBlock and
|
||||
not exists(ifstmt.getElse())
|
||||
select ifstmt, "This if-statement is redundant."
|
||||
@@ -0,0 +1,9 @@
|
||||
import cpp
|
||||
|
||||
predicate isEmpty(Block block) {
|
||||
block.isEmpty()
|
||||
}
|
||||
|
||||
from IfStmt ifstmt
|
||||
where isEmpty(ifstmt.getThen())
|
||||
select ifstmt, "This if-statement is redundant."
|
||||
@@ -0,0 +1,7 @@
|
||||
import cpp
|
||||
|
||||
from IfStmt ifstmt, Block block
|
||||
where
|
||||
block = ifstmt.getThen() and
|
||||
block.isEmpty()
|
||||
select ifstmt, "This if-statement is redundant."
|
||||
@@ -0,0 +1,13 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
|
||||
class TaintedFormatConfig extends TaintTracking::Configuration {
|
||||
TaintedFormatConfig() { this = "TaintedFormatConfig" }
|
||||
override predicate isSource(DataFlow::Node source) { /* TBD */ }
|
||||
override predicate isSink(DataFlow::Node sink) { /* TBD */ }
|
||||
}
|
||||
|
||||
from TaintedFormatConfig cfg, DataFlow::Node source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
select sink, "This format string may be derived from a $@.",
|
||||
source, "user-controlled value"
|
||||
11
docs/codeql/ql-training/query-examples/cpp/snprintf-1.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
|
||||
from FunctionCall call, DataFlow::Node source, DataFlow::Node sink
|
||||
where
|
||||
call.getTarget().getName() = "snprintf" and
|
||||
call.getArgument(2).getValue().regexpMatch("(?s).*%s.*") and
|
||||
TaintTracking::localTaint(source, sink) and
|
||||
source.asExpr() = call and
|
||||
sink.asExpr() = call.getArgument(1)
|
||||
select call
|
||||
@@ -0,0 +1,11 @@
|
||||
import java
|
||||
|
||||
class StringConcat extends AddExpr {
|
||||
StringConcat() { getType() instanceof TypeString }
|
||||
}
|
||||
|
||||
from MethodAccess ma
|
||||
where
|
||||
ma.getMethod().getName().matches("sparql%Query") and
|
||||
ma.getArgument(0) instanceof StringConcat
|
||||
select ma, "SPARQL query vulnerable to injection."
|
||||
@@ -0,0 +1,8 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
|
||||
from MethodAccess ma, StringConcat stringConcat
|
||||
where
|
||||
ma.getMethod().getName().matches("sparql%Query") and
|
||||
localFlow(exprNode(stringConcat), exprNode(ma.getArgument(0)))
|
||||
select ma, "SPARQL query vulnerable to injection."
|
||||
@@ -0,0 +1,12 @@
|
||||
import java
|
||||
|
||||
class EmptyBlock extends Block {
|
||||
EmptyBlock() {
|
||||
this.getNumStmt() = 0
|
||||
}
|
||||
}
|
||||
|
||||
from IfStmt ifstmt
|
||||
where ifstmt.getThen() instanceof
|
||||
EmptyBlock
|
||||
select ifstmt
|
||||
@@ -0,0 +1,11 @@
|
||||
import java
|
||||
|
||||
class EmptyBlock extends Block {
|
||||
EmptyBlock() { this.getNumStmt() = 0 }
|
||||
}
|
||||
|
||||
from IfStmt ifstmt
|
||||
where
|
||||
ifstmt.getThen() instanceof EmptyBlock and
|
||||
not exists(ifstmt.getElse())
|
||||
select ifstmt, "This if-statement is redundant."
|
||||
@@ -0,0 +1,9 @@
|
||||
import java
|
||||
|
||||
predicate isEmpty(Block block) {
|
||||
block.getNumStmt() = 0
|
||||
}
|
||||
|
||||
from IfStmt ifstmt
|
||||
where isEmpty(ifstmt.getThen())
|
||||
select ifstmt
|
||||
@@ -0,0 +1,7 @@
|
||||
import java
|
||||
|
||||
from IfStmt ifstmt, Block block
|
||||
where
|
||||
block = ifstmt.getThen() and
|
||||
block.getNumStmt() = 0
|
||||
select ifstmt, "This if-statement is redundant."
|
||||
@@ -0,0 +1,14 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
|
||||
class TaintedOGNLConfig extends TaintTracking::Configuration {
|
||||
TaintedOGNLConfig() { this = "TaintedOGNLConfig" }
|
||||
override predicate isSource(DataFlow::Node source) { /* TBD */ }
|
||||
override predicate isSink(DataFlow::Node sink) { /* TBD */ }
|
||||
}
|
||||
|
||||
from TaintedOGNLConfig cfg, DataFlow::Node source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
select source,
|
||||
"This untrusted input is evaluated as an OGNL expression $@.",
|
||||
sink, "here"
|
||||
@@ -0,0 +1,7 @@
|
||||
import java
|
||||
|
||||
from Method m, MethodAccess ma
|
||||
where
|
||||
m.getName().matches("sparql%Query") and
|
||||
ma.getMethod() = m
|
||||
select ma, m
|
||||