├── .gitignore
├── README.md
├── app.js
├── bin
└── www
├── package.json
├── public
├── index.html
├── js
│ ├── commands.js
│ └── jquery.term.js
└── stylesheets
│ ├── jquery.terminal.css
│ └── style.css
├── routes
├── index.js
└── users.js
└── views
├── error.jade
├── index.jade
├── layout.jade
└── main.jade
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | HackShell
2 | ============
3 |
4 | **HackShell** is a terminal simulator hacking game built in a 24-hour period at [CodeDay LA Spring 2014](http://codeday.org).
5 |
6 |
7 | The goal of the project was to create a fun game that introduces new users to the command line, through interactive levels where you play as a corporate hacker completing daring missions of corporate espionage. Learn common terminal commands and level up in this thrilling Cyberpunk adventure!
8 |
9 | ---
10 |
11 | Written in node.js and makes some use of [jquery.terminal](https://github.com/jcubic/jquery.terminal) for the terminal interface.
12 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var path = require('path');
3 | var favicon = require('static-favicon');
4 | var logger = require('morgan');
5 | var cookieParser = require('cookie-parser');
6 | var bodyParser = require('body-parser');
7 |
8 | var routes = require('./routes/index');
9 | var users = require('./routes/users');
10 |
11 | var app = express();
12 |
13 | // view engine setup
14 | app.set('views', path.join(__dirname, 'views'));
15 | app.set('view engine', 'jade');
16 |
17 | app.use(favicon());
18 | app.use(logger('dev'));
19 | app.use(bodyParser.json());
20 | app.use(bodyParser.urlencoded());
21 | app.use(cookieParser());
22 | app.use(express.static(path.join(__dirname, 'public')));
23 |
24 | app.use('/', function(req, res) {
25 | res.sendfile(path.join(__dirname, 'public/index.html'));
26 | });
27 | app.use('/users', users);
28 |
29 | /// catch 404 and forward to error handler
30 | app.use(function(req, res, next) {
31 | var err = new Error('Not Found');
32 | err.status = 404;
33 | next(err);
34 | });
35 |
36 | /// error handlers
37 |
38 | // development error handler
39 | // will print stacktrace
40 | if (app.get('env') === 'development') {
41 | app.use(function(err, req, res, next) {
42 | res.status(err.status || 500);
43 | res.render('error', {
44 | message: err.message,
45 | error: err
46 | });
47 | });
48 | }
49 |
50 | // production error handler
51 | // no stacktraces leaked to user
52 | app.use(function(err, req, res, next) {
53 | res.status(err.status || 500);
54 | res.render('error', {
55 | message: err.message,
56 | error: {}
57 | });
58 | });
59 |
60 |
61 | module.exports = app;
62 |
--------------------------------------------------------------------------------
/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | var debug = require('debug')('HackTerminal');
3 | var app = require('../app');
4 |
5 | app.set('port', process.env.PORT || 3000);
6 |
7 | var server = app.listen(app.get('port'), function() {
8 | debug('Express server listening on port ' + server.address().port);
9 | });
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "HackTerminal",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "express": "~4.2.0",
10 | "static-favicon": "~1.0.0",
11 | "morgan": "~1.0.0",
12 | "cookie-parser": "~1.0.1",
13 | "body-parser": "~1.0.0",
14 | "debug": "~0.7.4",
15 | "jade": "~1.3.0"
16 | }
17 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hackshell
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
14 |
16 |
17 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/public/js/commands.js:
--------------------------------------------------------------------------------
1 | var curDir = "~"
2 |
3 | var baseUrl = 'http://' + window.location.host;
4 | var dirs = {
5 | "~" : "Missions/",
6 | "~/Missions" : ""
7 | }
8 | var userfiles = {
9 | "~" : "readme.txt",
10 | "~/readme.txt" : "Hello there Mr.Kitnick, I'm Edmen Snowman and I'll be \nyour manager, prepare for utter death and destruction! (I'm kidding you know) \n\
11 | \n\
12 | This may be only your first day on the job, but we're in need of people like you \nso you better get started right away, in fact \nI may have a mission ready for you, check for it in your 'Missions' folder.",
13 | "~/Missions" : "mission1.txt",
14 | "~/Missions/mission1.txt" : "\n\nMission #1\n\
15 | ----------\n\n\
16 | Your mission, should you choose to accept it, is to find information \nfrom the computer of one of our own employees, Michael Groves.\n\
17 | Mr. Groves is suspected of working for our rival, Cortex Industries. \nCortex is in many ways a similar company to NitroSoft, but they are \nbuilding a so-called 'life improvement framework' which we beleive they may be planning \non using for brainwashing of all who use their services.\n\n\
18 | Procedure:\n\
19 | ----------\n\n\
20 | You will need to get into Groves's system, luckily we happen to have a\n\
21 | security camera right above all employee's desk, as is standard practice\n\
22 | and we captured " + baseUrl + "/clickable/test.html" + " picture, see if you can find anything from it.\n\
23 | When you have aquired his password, use the 'ssh' command like so to get into his system:\n\
24 | ssh mgroves@cortex.pizza "
25 |
26 | }
27 |
28 | var commands = function (input, cb) {
29 | var inParts = input.split(' ');
30 | var cmd = inParts[0];
31 | if(cmd === 'ls' || cmd === 'dir') {
32 | var lsr = ls(inParts[1]);
33 | return cb(lsr[0], lsr[1]);
34 | } else if(cmd === 'cd' || cmd === 'cd..') {
35 | if (cmd === 'cd..') {
36 | var cdr = cd("..")
37 | } else {
38 | var cdr = cd(inParts[1]);
39 | }
40 | return cb(cdr[0], cdr[1]);
41 | } else if(cmd === 'ssh') {
42 | var sshr = ssh(inParts[1], inParts[2], cb);
43 | } else if(cmd === 'help' || cmd === '?') {
44 | return cb(null, help());
45 | } else if(cmd === 'mail') {
46 | // Mail
47 | } else if(cmd === 'cat') {
48 | // Echo file
49 | var catr = cat(inParts[1]);
50 | return cb(catr[0], catr[1]);
51 | } else {
52 | return cb('No such function');
53 | }
54 | }
55 |
56 | // Callback structure:
57 | // cb(err, data);
58 |
59 | var ssh = function(whereto, pass, cb) {
60 | if (whereto === "mgroves@cortex.pizza" && pass === 'password1') {
61 | return cb(null, "Success! You have completed mission #1!");
62 | }
63 | return cb('Wrong user or pass!');
64 | }
65 |
66 | var help = function(cb) {
67 | var info = " ls - List files in current directory.\n \
68 | cd - Change to directory .\n \
69 | help - This.\n \
70 | cat - Output the contents of a file.\n \
71 | To get started, use the ls command to find any files, and cat to read them";
72 | return [null, info];
73 | }
74 |
75 | var ls = function(dir) {
76 | if (!dir){
77 | dir = curDir;
78 | }
79 | var dirlist = dirs[dir];
80 | var filelist = userfiles[dir];
81 | if (! dirlist){
82 | var list = [filelist];
83 | } else if (! filelist) {
84 | var list = [dirlist];
85 | } else {
86 | var list = [dirlist + "\n" + filelist];
87 | }
88 | return [null, list];
89 | }
90 |
91 | var cd = function(dir) {
92 | if (! dir) {
93 | curDir = "~";
94 | return [null, null];
95 | }
96 | if (dir.slice(-1) === "/") {
97 | dir = dir.substring(0, dir.length - 1);
98 | }
99 | if (dir === "..") {
100 | if (curDir === "~") {
101 | return ["Restricted, you may not go above your home directory"];
102 | }
103 | finDir = curDir.split("/");
104 | finDir.pop();
105 | curDir = finDir.join("/");
106 | } else {
107 | console.log(dirs[dir]);
108 | curDir = curDir + "/" + dir;
109 | }
110 | return [null, curDir];
111 | }
112 |
113 | var cat = function (file) {
114 | file = curDir + "/" + file;
115 | return [null, userfiles[file]]
116 | }
117 |
--------------------------------------------------------------------------------
/public/stylesheets/jquery.terminal.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This css file is part of jquery terminal
3 | *
4 | * Licensed under GNU LGPL Version 3 license
5 | * Copyright (c) 2011-2013 Jakub Jankiewicz
6 | *
7 | */
8 | .terminal .terminal-output .format, .cmd .format,
9 | .cmd .prompt, .cmd .prompt div, .terminal .terminal-output div div{
10 | display: inline-block;
11 | /*height: 100% !important;*/
12 | }
13 | .cmd .clipboard {
14 | position: absolute;
15 | bottom: 0;
16 | left: 0;
17 | opacity: 0.01;
18 | filter: alpha(opacity = 0.01);
19 | filter: progid:DXImageTransform.Microsoft.Alpha(opacity=0.01);
20 | width: 2px;
21 | /*height: 100% !important;*/
22 | }
23 | *{
24 | margin: 0;
25 | padding: 0;
26 | }
27 | /*.scanlines {
28 | background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAADElEQVQImWNggAJjAAA9ADTPmgqGAAAAAElFTkSuQmCC) repeat;
29 | z-index: 100000;
30 | }*/
31 | .cmd > .clipboard {
32 | position: fixed;
33 | }
34 | .terminal {
35 | padding: 10px;
36 | position: relative;
37 | overflow: hidden;
38 | /*height: 100% !important;*/
39 | }
40 | .cmd {
41 | padding: 0;
42 | margin: 0;
43 | height: 1.3em;
44 | /*margin-top: 3px; */
45 | }
46 | .cmd .cursor.blink {
47 | -webkit-animation: blink 1s infinite steps(1, start);
48 | -moz-animation: blink 1s infinite steps(1, start);
49 | -ms-animation: blink 1s infinite steps(1, start);
50 | animation: blink 1s infinite steps(1, start);
51 | }
52 | @keyframes blink {
53 | 0%, 100% {
54 | background-color: #000;
55 | color: #aaa;
56 | }
57 | 50% {
58 | background-color: #bbb; /* not #aaa because it's seem there is Google Chrome bug */
59 | color: #000;
60 | }
61 | }
62 | @-webkit-keyframes blink {
63 | 0%, 100% {
64 | background-color: #000;
65 | color: #aaa;
66 | }
67 | 50% {
68 | background-color: #bbb;
69 | color: #000;
70 | }
71 | }
72 | @-ms-keyframes blink {
73 | 0%, 100% {
74 | background-color: #000;
75 | color: #aaa;
76 | }
77 | 50% {
78 | background-color: #bbb;
79 | color: #000;
80 | }
81 | }
82 | @-moz-keyframes blink {
83 | 0%, 100% {
84 | background-color: #000;
85 | color: #aaa;
86 | }
87 | 50% {
88 | background-color: #bbb;
89 | color: #000;
90 | }
91 | }
92 | .terminal .terminal-output div div, .cmd .prompt {
93 | display: block;
94 | line-height: 25px;
95 | height: auto;
96 | }
97 | .cmd .prompt {
98 | float: left;
99 | }
100 | $scanLineWidth: 1px;
101 | .scanLines {
102 | /*** WEBKIT ***/
103 | background: -webkit-repeating-linear-gradient(
104 | top,
105 | transparent 0px,
106 | transparent $scanLineWidth,
107 | rgba(0,0,0,0.25) $scanLineWidth,
108 | rgba(0,0,0,0.25) $scanLineWidth*2
109 | );
110 | -webkit-background-size: 100% $scanLineWidth*2;
111 | /** MOZILLA **/
112 | background: -moz-repeating-linear-gradient(
113 | top,
114 | transparent 0px,
115 | transparent $scanLineWidth,
116 | rgba(0,0,0,0.25) $scanLineWidth,
117 | rgba(0,0,0,0.25) $scanLineWidth*2
118 | );
119 | -moz-background-size: 100% $scanLineWidth*2;
120 | }
121 | .terminal, .cmd {
122 | font-family: Courier New, monospace;
123 | color: #1f1;
124 | text-shadow: 2px 1px 1px #F05,
125 | -2px -1px 1px #05F;
126 | background-color: #111;
127 | font-size: 20px;
128 | line-height: 25px;
129 | font-weight: bold;
130 | height 100% !important;
131 | }
132 | .terminal-output > div {
133 | /*padding-top: 3px;*/
134 | min-height: 14px;
135 | }
136 | .terminal .terminal-output div span {
137 | display: inline-block;
138 | }
139 | .cmd span {
140 | float: left;
141 | /*display: inline-block; */
142 | }
143 | .terminal .inverted, .cmd .inverted, .cmd .cursor.blink {
144 | background-color: #aaa;
145 | color: #000;
146 | }
147 | .terminal .terminal-output div div::-moz-selection,
148 | .terminal .terminal-output div span::-moz-selection,
149 | .terminal .terminal-output div div a::-moz-selection {
150 | background-color: #aaa;
151 | color: #000;
152 | }
153 | .terminal .terminal-output div div::selection,
154 | .terminal .terminal-output div div a::selection,
155 | .terminal .terminal-output div span::selection,
156 | .cmd > span::selection,
157 | .cmd .prompt span::selection {
158 | background-color: #aaa;
159 | color: #000;
160 | }
161 | .terminal .terminal-output div.error, .terminal .terminal-output div.error div {
162 | color: red;
163 | }
164 | .tilda {
165 | position: fixed;
166 | top: 0;
167 | left: 0;
168 | width: 100%;
169 | z-index: 1100;
170 | }
171 | .clear {
172 | clear: both;
173 | }
174 | .terminal a {
175 | color: #0F60FF;
176 | }
177 | .terminal a:hover {
178 | color: red;
179 | }
180 |
--------------------------------------------------------------------------------
/public/stylesheets/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 50px;
3 | font: 24px "Lucida Grande", Helvetica, Arial, sans-serif;
4 | }
5 |
6 | html {
7 | background-color: #111 !important;
8 | }
9 |
10 | a {
11 | color: #00B7FF;
12 | }
13 |
--------------------------------------------------------------------------------
/routes/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET home page. */
5 | router.get('/', function(req, res) {
6 | res.render('index', { title: 'Express' });
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/routes/users.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var router = express.Router();
3 |
4 | /* GET users listing. */
5 | router.get('/', function(req, res) {
6 | res.send('respond with a resource');
7 | });
8 |
9 | module.exports = router;
10 |
--------------------------------------------------------------------------------
/views/error.jade:
--------------------------------------------------------------------------------
1 | extends layout
2 |
3 | block content
4 | h1= message
5 | h2= error.status
6 | pre #{error.stack}
7 |
--------------------------------------------------------------------------------
/views/index.jade:
--------------------------------------------------------------------------------
1 | extends layout
2 |
3 | block content
4 | h1= title
5 | p Welcome to #{title}
6 |
--------------------------------------------------------------------------------
/views/layout.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | title= title
5 | link(rel='stylesheet', href='/stylesheets/style.css')
6 | body
7 | block content
--------------------------------------------------------------------------------
/views/main.jade:
--------------------------------------------------------------------------------
1 | doctype html
2 | html
3 | head
4 | script(src='//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js')
5 | script(src='http://terminal.jcubic.pl/js/jquery.terminal-0.8.7.min.js')
6 | script(src='https://raw.githubusercontent.com/HackShellApp/HackShellWeb/master/commands.js')
7 | script(src='https://gist.githubusercontent.com/caffeinewriter/efa9f1ca20f0eb841d35/raw/cd95a39a332f378e89c5c29e84314508d692a944/gistfile1.js')
8 | body
9 | #terminal
10 |
--------------------------------------------------------------------------------