inspec/www/tutorial/app/app.component.ts

233 lines
7.9 KiB
TypeScript
Raw Normal View History

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';
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 {
// template values
2016-08-24 12:58:40 +00:00
instructions: any;
title: string;
// taken as input by xterm terminal componen
response: string;
shell: string;
2016-08-24 12:58:40 +00:00
// colors for responses
red: string = "";
white: string = "";
black: string = "";
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
commands: any = [];
instructionsArray: any = [];
2016-08-24 12:58:40 +00:00
constructor(private http: Http) { }
ngOnInit() {
// load json files
2016-08-24 12:58:40 +00:00
this.getInstructions();
this.getCommands();
2016-08-24 12:58:40 +00:00
}
ngAfterViewChecked() {
window.scrollTo( 0, document.body.scrollHeight );
}
// 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) {
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;
}
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
}
this.response = this.black + 'prev' + msg;
} else if (step === 'last') {
this.counter = totalSteps - 1
this.response = this.black + 'last' + msg;
}
this.displayInstructions();
}
extraCmds() {
let cmds = this.commands
let extra = Object.keys(cmds).filter(function(key){
return cmds[key]['extra'] == true
});
return extra
}
// 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!";
this.instructions = "here are some other commands you can try out: \r\n\r\n" + this.extraCmds();
} else {
if (this.instructionsArray[this.counter][1]) {
this.title = this.instructionsArray[this.counter][0];
this.instructions = this.instructionsArray[this.counter][1];
} else {
this.instructions = 'Sorry, something seems to have gone wrong. Please try refreshing your browser.';
}
2016-09-02 19:13:07 +00:00
}
}
formatInstructions() {
return this.instructions || '';
}
// 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');
}
else if (command.match(/^last\s*/)) {
this.updateInstructions('last');
}
else if (this.shell === 'inspec-shell') {
this.parseInspecShell(command);
}
else {
this.checkCommand(command);
2016-09-02 19:13:07 +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
}
else if (command.match(/^pwd\s*/)) {
this.response = this.white + "anonymous-web-user/inspec-shell";
2016-09-02 19:13:07 +00:00
}
else {
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);
}
}
// takes the command as input, replaces all white space with regex whitespace matcher
// and creates a new regex. check if the regex matches any of the keys in the commands
// 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;
// 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));
}
}
// 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;
}
}
}
// 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
}
let msg = Math.random();
this.response = this.white + data['_body'] + this.black + msg;
2016-08-24 12:58:40 +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() {
this.http.get('app/responses/instructions.json')
.subscribe(data => {
this.instructionsArray = JSON.parse(data['_body']);
this.displayInstructions();
2016-08-24 12:58:40 +00:00
},
err => console.error(err)
);
}
// load json file for commands and push each object to commands
getCommands() {
this.http.get('app/responses/commands.json')
.subscribe(data => {
let result = JSON.parse(data['_body']);
this.commands = result
},
err => console.error(err)
);
}
}