2016-08-29 21:12:31 +00:00
|
|
|
|
import { Component, OnInit } from '@angular/core';
|
2016-08-24 12:58:40 +00:00
|
|
|
|
import { Http, Response } from '@angular/http';
|
|
|
|
|
import { Observable } from 'rxjs/Rx';
|
|
|
|
|
import { HTTP_PROVIDERS } from '@angular/http';
|
2016-08-29 21:12:31 +00:00
|
|
|
|
import { XtermTerminalComponent } from './xterm-terminal/xterm-terminal.component';
|
2016-09-06 14:32:24 +00:00
|
|
|
|
declare var require: any;
|
|
|
|
|
var shellwords = require("shellwords");
|
2016-08-24 12:58:40 +00:00
|
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
|
selector: 'my-app',
|
|
|
|
|
templateUrl: 'app/app.component.html',
|
|
|
|
|
styleUrls: ['app/app.component.css'],
|
|
|
|
|
providers: [ HTTP_PROVIDERS ],
|
2016-08-29 21:12:31 +00:00
|
|
|
|
directives: [ XtermTerminalComponent ]
|
2016-08-24 12:58:40 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export class AppComponent implements OnInit {
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// template values
|
2016-08-24 12:58:40 +00:00
|
|
|
|
instructions: any;
|
2016-09-06 14:32:24 +00:00
|
|
|
|
title: string;
|
|
|
|
|
|
|
|
|
|
// taken as input by xterm terminal componen
|
2016-08-30 21:02:55 +00:00
|
|
|
|
response: string;
|
|
|
|
|
shell: string;
|
2016-08-24 12:58:40 +00:00
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// colors for responses
|
2016-09-13 11:08:13 +00:00
|
|
|
|
red: string = "[31m";
|
|
|
|
|
white: string = "[37m";
|
|
|
|
|
black: string = "[30m";
|
2016-09-06 14:32:24 +00:00
|
|
|
|
|
|
|
|
|
matchFound: boolean; // helps to handle no match found response
|
|
|
|
|
counter: number = 0; // keeps track of step number count
|
|
|
|
|
userCommand: string; // used to display better error msg when no match is found
|
|
|
|
|
|
|
|
|
|
// arrays of data parsed from json files
|
2016-09-14 07:18:25 +00:00
|
|
|
|
commands: any = [];
|
2016-09-06 14:32:24 +00:00
|
|
|
|
instructionsArray: any = [];
|
|
|
|
|
|
2016-08-24 12:58:40 +00:00
|
|
|
|
constructor(private http: Http) { }
|
|
|
|
|
|
|
|
|
|
ngOnInit() {
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// load json files
|
2016-08-24 12:58:40 +00:00
|
|
|
|
this.getInstructions();
|
2016-09-14 07:18:25 +00:00
|
|
|
|
this.getCommands();
|
2016-08-24 12:58:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 11:08:13 +00:00
|
|
|
|
ngAfterViewChecked() {
|
|
|
|
|
window.scrollTo( 0, document.body.scrollHeight );
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// called when command entered is 'next' or 'prev'
|
|
|
|
|
// modifies value of counter and calls displayInstructions
|
2016-08-29 21:12:31 +00:00
|
|
|
|
updateInstructions(step) {
|
2016-08-30 21:02:55 +00:00
|
|
|
|
let totalSteps = this.instructionsArray.length - 1;
|
2016-09-02 19:13:07 +00:00
|
|
|
|
let msg = Math.random();
|
2016-09-01 15:19:04 +00:00
|
|
|
|
if (step === 'next') {
|
2016-09-02 19:13:07 +00:00
|
|
|
|
if (this.counter < totalSteps) {
|
2016-09-01 15:19:04 +00:00
|
|
|
|
this.counter += 1;
|
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
this.response = this.black + 'next' + msg;
|
2016-09-01 15:19:04 +00:00
|
|
|
|
} else if (step === 'prev') {
|
2016-09-02 19:13:07 +00:00
|
|
|
|
if (this.counter > 0) {
|
|
|
|
|
this.counter -= 1;
|
2016-09-01 15:19:04 +00:00
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
this.response = this.black + 'prev' + msg;
|
2016-09-14 07:18:25 +00:00
|
|
|
|
} else if (step === 'last') {
|
|
|
|
|
this.counter = totalSteps - 1
|
|
|
|
|
this.response = this.black + 'last' + msg;
|
2016-08-30 21:02:55 +00:00
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
this.displayInstructions();
|
2016-08-30 21:02:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-14 07:18:25 +00:00
|
|
|
|
extraCmds() {
|
|
|
|
|
let cmds = this.commands
|
|
|
|
|
let extra = Object.keys(cmds).filter(function(key){
|
|
|
|
|
return cmds[key]['extra'] == true
|
|
|
|
|
});
|
|
|
|
|
return extra
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// display instructions based on value of counter and
|
|
|
|
|
// format text to remove triple backticks. if the user has reached
|
|
|
|
|
// then end of the demo, display a message containing extra commands that have been
|
|
|
|
|
// enabled in the demo
|
|
|
|
|
displayInstructions() {
|
|
|
|
|
if (this.counter === this.instructionsArray.length - 1) {
|
|
|
|
|
this.title = "the end; that's all folks!";
|
2016-09-14 07:18:25 +00:00
|
|
|
|
this.instructions = "here are some other commands you can try out: \r\n\r\n" + this.extraCmds();
|
2016-09-06 14:32:24 +00:00
|
|
|
|
} else {
|
|
|
|
|
if (this.instructionsArray[this.counter][1]) {
|
|
|
|
|
this.title = this.instructionsArray[this.counter][0];
|
2016-09-13 11:08:13 +00:00
|
|
|
|
this.instructions = this.instructionsArray[this.counter][1];
|
2016-09-06 14:32:24 +00:00
|
|
|
|
} else {
|
|
|
|
|
this.instructions = 'Sorry, something seems to have gone wrong. Please try refreshing your browser.';
|
|
|
|
|
}
|
2016-09-02 19:13:07 +00:00
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-13 11:08:13 +00:00
|
|
|
|
formatInstructions() {
|
|
|
|
|
return this.instructions || '';
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// called when a new value is emitted for command
|
|
|
|
|
// checks for a match, calls parseInspecShell if shell is inspec-shell
|
|
|
|
|
// and calls checkCommand if none of the first commands match
|
|
|
|
|
evalCommand(command) {
|
|
|
|
|
this.userCommand = command;
|
|
|
|
|
if (command.match(/^next\s*/)) {
|
2016-09-02 19:13:07 +00:00
|
|
|
|
this.updateInstructions('next');
|
|
|
|
|
}
|
|
|
|
|
else if (command.match(/^prev\s*/)) {
|
|
|
|
|
this.updateInstructions('prev');
|
|
|
|
|
}
|
2016-09-14 07:18:25 +00:00
|
|
|
|
else if (command.match(/^last\s*/)) {
|
|
|
|
|
this.updateInstructions('last');
|
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
else if (this.shell === 'inspec-shell') {
|
|
|
|
|
this.parseInspecShell(command);
|
|
|
|
|
}
|
2016-08-30 21:02:55 +00:00
|
|
|
|
else {
|
2016-09-06 14:32:24 +00:00
|
|
|
|
this.checkCommand(command);
|
2016-09-02 19:13:07 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// if the shell is inspec-shell, we want to exit the shell env on 'exit'
|
|
|
|
|
// format the command for checkCommand by using shellwords.escape and
|
|
|
|
|
// adding 'echo' and 'inspec shell' to match command from commands.json
|
|
|
|
|
parseInspecShell(command) {
|
|
|
|
|
if (command.match(/^exit\s*/)) {
|
|
|
|
|
this.shell = '';
|
|
|
|
|
this.response = '';
|
2016-09-02 19:13:07 +00:00
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
else if (command.match(/^pwd\s*/)) {
|
|
|
|
|
this.response = this.white + "anonymous-web-user/inspec-shell";
|
2016-09-02 19:13:07 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2016-09-06 14:32:24 +00:00
|
|
|
|
let escaped_cmd;
|
|
|
|
|
let formatted_cmd;
|
|
|
|
|
// TODO: make this better
|
|
|
|
|
// I don't really like what we're doing here when we have a
|
|
|
|
|
// describe and control block, but I had a lot of trouble getting this
|
|
|
|
|
// to work because of the new lines carriage returns in the command from commands.json
|
|
|
|
|
// so at the moment we're splitting the command on "do" and assuming a match if that first
|
|
|
|
|
// result group matches (everything before do) :/
|
|
|
|
|
if (command.match(/^describe.*|^control/)) {
|
|
|
|
|
let split_cmd = command.split('do');
|
|
|
|
|
escaped_cmd = shellwords.escape(split_cmd[0]);
|
|
|
|
|
formatted_cmd = 'echo.*' + escaped_cmd ;
|
|
|
|
|
} else {
|
|
|
|
|
escaped_cmd = shellwords.escape(command)
|
|
|
|
|
formatted_cmd = 'echo.*' + escaped_cmd + '.*inspec.*shell';
|
|
|
|
|
}
|
|
|
|
|
let regex_compatible = formatted_cmd.replace(/\W+/g, '.*');
|
|
|
|
|
this.checkCommand(regex_compatible);
|
2016-08-30 21:02:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// takes the command as input, replaces all white space with regex whitespace matcher
|
2016-09-14 07:18:25 +00:00
|
|
|
|
// and creates a new regex. check if the regex matches any of the keys in the commands
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// if it matches, we set matchFound to true and call displayResult. if it doesn't match,
|
|
|
|
|
// we display a default error message
|
|
|
|
|
checkCommand(command) {
|
|
|
|
|
let dir = 'app/responses/';
|
|
|
|
|
let cmd = command.replace(/ /g,"\\s*")
|
|
|
|
|
let regexcmd = new RegExp(('^'+cmd+'$'), 'm')
|
|
|
|
|
this.matchFound = false;
|
2016-09-14 07:18:25 +00:00
|
|
|
|
|
|
|
|
|
// iterate over commands and try to match the command with the input
|
|
|
|
|
let cmds = Object.keys(this.commands)
|
|
|
|
|
for (var i = 0; i < cmds.length; i++) {
|
|
|
|
|
let cmd = cmds[i];
|
|
|
|
|
if (cmd.match(regexcmd)) {
|
|
|
|
|
this.matchFound = true;
|
|
|
|
|
let key = this.commands[cmd]['key'];
|
|
|
|
|
this.http.get(dir + key).subscribe(data => {
|
|
|
|
|
this.displayResult(command, data);
|
|
|
|
|
},
|
|
|
|
|
err => console.error(err));
|
2016-09-06 14:32:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// if no match is found, we check if the command entered was inspec exec something
|
|
|
|
|
// and if it is respond appropriately ('could not fetch inspec profile in ''), otherwise
|
|
|
|
|
// respond with 'invalid command' and the command entered
|
|
|
|
|
if (this.matchFound === false) {
|
|
|
|
|
let msg = Math.random();
|
|
|
|
|
if (command.match(/^inspec exec\s*.*/)) {
|
|
|
|
|
let target = command.match(/^inspec exec\s*(.*)/)
|
|
|
|
|
if (target[1].match(/.*-t.*|.*-b.*/)) {
|
|
|
|
|
this.response = this.red + "Sorry, we haven't figured out how to handle the transport options in this demo just yet." + this.black + msg;
|
|
|
|
|
} else {
|
|
|
|
|
this.response = this.red + "Could not fetch inspec profile in '" + target[1] + "' " + this.black + msg;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.response = this.red + 'invalid command: ' + this.userCommand + this.black + msg;
|
|
|
|
|
}
|
2016-08-30 21:02:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// if the command if inspec shell, we also need to set the shell variable to
|
|
|
|
|
// inspec shell. print response value and random msg (to ensure recognition of value change by xterm
|
|
|
|
|
// terminal component)
|
|
|
|
|
displayResult(command, data) {
|
|
|
|
|
if (command.match(/^inspec\s*shell\s*$/)) {
|
|
|
|
|
this.shell = 'inspec-shell';
|
2016-08-29 21:12:31 +00:00
|
|
|
|
}
|
2016-09-06 14:32:24 +00:00
|
|
|
|
let msg = Math.random();
|
|
|
|
|
this.response = this.white + data['_body'] + this.black + msg;
|
2016-08-24 12:58:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-09-06 14:32:24 +00:00
|
|
|
|
// load json file for instructions and save to instructionsArray
|
|
|
|
|
// call displayInstructions to load first set of instructions
|
2016-08-24 12:58:40 +00:00
|
|
|
|
getInstructions() {
|
2016-09-14 07:18:25 +00:00
|
|
|
|
this.http.get('app/responses/instructions.json')
|
2016-09-06 14:32:24 +00:00
|
|
|
|
.subscribe(data => {
|
|
|
|
|
this.instructionsArray = JSON.parse(data['_body']);
|
|
|
|
|
this.displayInstructions();
|
2016-08-24 12:58:40 +00:00
|
|
|
|
},
|
|
|
|
|
err => console.error(err)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-14 07:18:25 +00:00
|
|
|
|
// load json file for commands and push each object to commands
|
|
|
|
|
getCommands() {
|
|
|
|
|
this.http.get('app/responses/commands.json')
|
2016-09-06 14:32:24 +00:00
|
|
|
|
.subscribe(data => {
|
|
|
|
|
let result = JSON.parse(data['_body']);
|
2016-09-14 07:18:25 +00:00
|
|
|
|
this.commands = result
|
2016-09-06 14:32:24 +00:00
|
|
|
|
},
|
|
|
|
|
err => console.error(err)
|
|
|
|
|
);
|
|
|
|
|
}
|
2016-09-14 07:18:25 +00:00
|
|
|
|
}
|