mirror of
https://github.com/inspec/inspec
synced 2024-11-26 22:50:36 +00:00
refactor terminal
This commit is contained in:
parent
b8cc700495
commit
bc2ac4a80a
21 changed files with 601 additions and 420 deletions
|
@ -1,13 +1,9 @@
|
|||
.main {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.terminal-nav {
|
||||
.tutorial-nav {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
top: 10;
|
||||
right: 0;
|
||||
width: 120px;
|
||||
width: 150px;
|
||||
height: 60px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
|
@ -25,23 +21,38 @@
|
|||
|
||||
img {
|
||||
padding-top: 5px;
|
||||
width: 160px;
|
||||
height: 40px;
|
||||
width: 100px;
|
||||
padding-bottom: 5px;
|
||||
position: fixed;
|
||||
bottom: 10;
|
||||
right: 30px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.guide {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
.tutorial {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.tutorial-wrapper {
|
||||
max-width: 1000px;
|
||||
min-width: 400px;
|
||||
margin: 10px auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tutorial-guide {
|
||||
padding: 10px;
|
||||
color: #DDDDDD;
|
||||
background-color: #444;
|
||||
font-family: 'Ubuntu Mono', 'Monaco', sans-serif;
|
||||
letter-spacing: 1.2px;
|
||||
font-size: 15px;
|
||||
max-width: 1000px;
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
background-color: #444;
|
||||
text-align: left;
|
||||
line-height: 1.4;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -57,15 +68,13 @@ I'm unsure why, but attempting to set the style of code in this setting is just
|
|||
not working....but this works! */
|
||||
:host /deep/ code {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.cli {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: monospace;
|
||||
font-size: 1.2rem;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
position: fixed;
|
||||
top: 250px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
<div class="terminal-nav">
|
||||
<span (click)="updateInstructions('prev')"> < </span>
|
||||
<span (click)="updateInstructions('next')"> > </span>
|
||||
<span> x </span>
|
||||
<div class="tutorial">
|
||||
<div class="tutorial-nav">
|
||||
<i (click)="updateInstructions('prev')" class="icon-angle-circled-left"></i>
|
||||
<i (click)="updateInstructions('next')" class="icon-angle-circled-right"></i>
|
||||
<i class="icon-cancel-circled"></i>
|
||||
</div>
|
||||
|
||||
<div class="tutorial-wrapper">
|
||||
<div class="tutorial-guide">
|
||||
<p class="title">{{title}}</p>
|
||||
<p [innerHTML]="formatInstructions()"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img src="inspec-logo.png" alt="Inspec Logo">
|
||||
<div class="main">
|
||||
<div class="guide">
|
||||
<p class="title">{{title}}</p>
|
||||
<p [innerHTML]="formatInstructions()"></p>
|
||||
</div>
|
||||
<xterm-terminal class="cli" (stdin)="evalCommand(command=$event)" [stdout]="stdout" [prompt]="prompt"></xterm-terminal>
|
||||
|
||||
<div class="cli">
|
||||
<xterm-terminal (command)="evalCommand(command=$event)" [response]="response" [shell]="shell"></xterm-terminal>
|
||||
</div>
|
||||
</div>
|
||||
<img src="inspec-logo-white.png" alt="InSpec Logo">
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, EventEmitter } from '@angular/core';
|
||||
import { Http, Response } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { HTTP_PROVIDERS } from '@angular/http';
|
||||
import { XtermTerminalComponent } from './xterm-terminal/xterm-terminal.component';
|
||||
declare var require: any;
|
||||
var shellwords = require("shellwords");
|
||||
|
||||
const SH_PROMPT = '[36m$[0m ';
|
||||
const INSPEC_PROMPT = '[0;32minspec> [0m';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
|
@ -15,170 +17,110 @@ var shellwords = require("shellwords");
|
|||
})
|
||||
|
||||
export class AppComponent implements OnInit {
|
||||
// template values
|
||||
instructions: any;
|
||||
|
||||
// all available commands parsed from json files
|
||||
commands: any = [];
|
||||
// we support two shell modes: 'sh' and 'inspec'
|
||||
shellMode: string = 'sh'
|
||||
|
||||
// all tutorial instructions
|
||||
instructionsArray: any = [];
|
||||
// title of the current tutorial step
|
||||
title: string;
|
||||
// instruction of the current step
|
||||
instruction: any;
|
||||
// keeps track of step number count
|
||||
counter: number = 0;
|
||||
|
||||
// taken as input by xterm terminal componen
|
||||
response: string;
|
||||
shell: string;
|
||||
stdout: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
// blue regular shell prompt
|
||||
prompt: string = SH_PROMPT;
|
||||
|
||||
// colors for responses
|
||||
red: string = "[31m";
|
||||
white: string = "[37m";
|
||||
black: string = "[30m";
|
||||
|
||||
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 = [];
|
||||
// inspec shell depth
|
||||
shell_depth = 0;
|
||||
shell_command = ''
|
||||
|
||||
constructor(private http: Http) { }
|
||||
|
||||
ngOnInit() {
|
||||
// load json files
|
||||
this.getInstructions();
|
||||
this.getCommands();
|
||||
// load content
|
||||
this.loadInstructions();
|
||||
this.loadCommands();
|
||||
}
|
||||
|
||||
ngAfterViewChecked() {
|
||||
window.scrollTo( 0, document.body.scrollHeight );
|
||||
}
|
||||
|
||||
// called when command entered is 'next' or 'prev'
|
||||
// modifies value of counter and calls displayInstructions
|
||||
updateInstructions(step) {
|
||||
let totalSteps = this.instructionsArray.length - 1;
|
||||
let msg = Math.random();
|
||||
if (step === 'next') {
|
||||
if (this.counter <= totalSteps) {
|
||||
this.counter += 1;
|
||||
}
|
||||
this.response = this.black + 'next' + msg;
|
||||
} else if (step === 'prev') {
|
||||
if (this.counter > 0) {
|
||||
this.counter -= 1;
|
||||
}
|
||||
this.response = this.black + 'prev' + msg;
|
||||
} else if (step === 'last') {
|
||||
this.counter = totalSteps;
|
||||
this.response = this.black + 'last' + msg;
|
||||
}
|
||||
|
||||
this.displayInstructions();
|
||||
}
|
||||
|
||||
// determines all commands that are not part of the tutorial
|
||||
extraCmds() {
|
||||
let cmds = this.commands
|
||||
let extra = Object.keys(cmds).filter(function(key){
|
||||
return cmds[key]['extra'] == true
|
||||
let extra = this.commands.filter(function(item){
|
||||
return item['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) {
|
||||
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.';
|
||||
}
|
||||
}
|
||||
return extra.map(function(item){return item.command})
|
||||
}
|
||||
|
||||
formatInstructions() {
|
||||
return this.instructions || '';
|
||||
return this.instruction || '';
|
||||
}
|
||||
|
||||
// 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*/)) {
|
||||
this.updateInstructions('next');
|
||||
// called when tutorial commands need to be displayed
|
||||
updateInstructions(step = null) {
|
||||
// if step is given, we calculate the new index
|
||||
let totalSteps = this.instructionsArray.length - 1;
|
||||
switch(step) {
|
||||
case "next":
|
||||
if (this.counter < totalSteps) {
|
||||
this.counter += 1;
|
||||
}
|
||||
break;
|
||||
case 'prev':
|
||||
if (this.counter > 0) {
|
||||
this.counter -= 1;
|
||||
}
|
||||
break;
|
||||
case 'first':
|
||||
this.counter = 1;
|
||||
break
|
||||
case 'last':
|
||||
this.counter = totalSteps;
|
||||
break
|
||||
}
|
||||
else if (command.match(/^prev\s*/)) {
|
||||
this.updateInstructions('prev');
|
||||
}
|
||||
else if (command.match(/^last\s*/)) {
|
||||
this.updateInstructions('last');
|
||||
}
|
||||
else if (command.match(/^inspec\s*shell\s*$/)) {
|
||||
this.shell = 'inspec-shell';
|
||||
this.response = this.white + 'Welcome to the interactive InSpec Shell\r\nTo find out how to use it, type: help\r\nTo exit, type: exit\r\n';
|
||||
}
|
||||
else if (this.shell === 'inspec-shell') {
|
||||
this.parseInspecShell(command);
|
||||
}
|
||||
else {
|
||||
this.checkCommand(command);
|
||||
|
||||
if (this.counter === this.instructionsArray.length - 1) {
|
||||
this.title = "the end; that's all folks!";
|
||||
this.instruction = "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.instruction = this.instructionsArray[this.counter][1];
|
||||
} else {
|
||||
this.instruction = 'Sorry, something seems to have gone wrong. Please try refreshing your browser.';
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = '';
|
||||
}
|
||||
else if (command.match(/^pwd\s*/)) {
|
||||
this.response = this.white + "anonymous-web-user/inspec-shell";
|
||||
}
|
||||
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,
|
||||
// takes the command as input. checks if the regex matches any of the keys in the commands
|
||||
// if it matches, we set matchFound to true and call printOnStdout. if it doesn't match,
|
||||
// we display a default error message
|
||||
checkCommand(command) {
|
||||
execCommand(command, shell) {
|
||||
let response = ''
|
||||
let dir = 'app/responses/';
|
||||
let cmd = command.replace(/ /g,'\\s*')
|
||||
let regexcmd = new RegExp(('^'+cmd+'$'), 'm')
|
||||
this.matchFound = false;
|
||||
|
||||
var 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);
|
||||
for (var i = 0; i < this.commands.length; i++) {
|
||||
let item = this.commands[i]
|
||||
if (item.regex.exec(command) && item.shell == shell) {
|
||||
matchFound = true;
|
||||
this.http.get(dir + item.key).subscribe(data => {
|
||||
this.printOnStdout(data['_body'])
|
||||
},
|
||||
err => console.error(err));
|
||||
}
|
||||
|
@ -186,46 +128,119 @@ export class AppComponent implements OnInit {
|
|||
// 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 (matchFound === false) {
|
||||
console.log('no match found')
|
||||
if (command.match(/^inspec exec\s*.*/)) {
|
||||
let target = command.match(/^inspec exec\s*(.*)/)
|
||||
this.response = this.red + "Could not fetch inspec profile in '" + target[1] + "' " + this.black + msg;
|
||||
response = this.red + "Could not fetch inspec profile in '" + target[1] + "' " + this.black;
|
||||
} else {
|
||||
this.response = this.red + 'invalid command: ' + this.userCommand + this.black + msg;
|
||||
response = this.red + 'command not found: ' + command;
|
||||
}
|
||||
this.printOnStdout(response + "\n\r")
|
||||
}
|
||||
}
|
||||
|
||||
// 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';
|
||||
// handles a stdin command and prints on terminal stdout
|
||||
evalCommand(command) {
|
||||
let self = this
|
||||
// tutorial commands
|
||||
var m = /^\s*(next|prev|first|last)\s*$/.exec(command)
|
||||
let multiline = /\s*(describe|control|end)\s*/.exec(command)
|
||||
if (m) {
|
||||
// update the instructions widget
|
||||
// we are not calling this from the ng event loop, therefore we need to
|
||||
// trigger angular to update the tutorial section
|
||||
setTimeout(function(){
|
||||
self.updateInstructions(m[1]);
|
||||
}, 0);
|
||||
// send an empty response to get the command prompt back
|
||||
this.printOnStdout('')
|
||||
}
|
||||
let msg = Math.random();
|
||||
this.response = this.white + data['_body'] + this.black + msg;
|
||||
// switch to InSpec shell commands
|
||||
// TODO, support targets for InSpec shell
|
||||
else if (/^\s*inspec\s*shell\s*$/.exec(command)) {
|
||||
// switch to InSpec shell
|
||||
this.shellMode = 'inspec'
|
||||
// switch prompt
|
||||
this.prompt = INSPEC_PROMPT;
|
||||
// output inspec hello text
|
||||
let init = "Welcome to the interactive InSpec Shell\n\rTo find out how to use it, type: [1mhelp[0m\n\r"
|
||||
this.printOnStdout(init);
|
||||
}
|
||||
// leave InSpec shell
|
||||
else if (/^\s*exit\s*$/.exec(command) && this.shellMode == 'inspec') {
|
||||
this.shellMode = 'sh'
|
||||
// switch prompt
|
||||
this.prompt = SH_PROMPT;
|
||||
this.printOnStdout("\n\r");
|
||||
}
|
||||
else if (this.shellMode == 'inspec' && multiline) {
|
||||
// count control + describe
|
||||
let mstart = command.match(/describe|control/g)
|
||||
if (mstart) {
|
||||
this.shell_depth += mstart.length
|
||||
}
|
||||
// end
|
||||
let mend = command.match(/end/g)
|
||||
if (mend) {
|
||||
this.shell_depth -= mend.length
|
||||
}
|
||||
|
||||
this.shell_command += command + "\n"
|
||||
|
||||
if (mend && this.shell_depth == 0) {
|
||||
command = this.shell_command
|
||||
this.shell_depth = 0
|
||||
this.shell_command = ''
|
||||
this.execCommand(command, this.shellMode);
|
||||
}
|
||||
}
|
||||
// default sh commands
|
||||
else if ((this.shell_depth == 0) || this.shellMode == 'sh') {
|
||||
this.execCommand(command, this.shellMode);
|
||||
}
|
||||
// store command in cache, must be an inspec shell command
|
||||
else {
|
||||
this.shell_command += command + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// submit stdout data to terminal
|
||||
printOnStdout(data){
|
||||
this.stdout.emit(data)
|
||||
this.stdout.emit(this.prompt)
|
||||
}
|
||||
|
||||
// load json file for instructions and save to instructionsArray
|
||||
// call displayInstructions to load first set of instructions
|
||||
getInstructions() {
|
||||
// call updateInstructions to load first set of instructions
|
||||
loadInstructions() {
|
||||
let self = this
|
||||
this.http.get('app/responses/instructions.json')
|
||||
.subscribe(data => {
|
||||
this.instructionsArray = JSON.parse(data['_body']);
|
||||
this.displayInstructions();
|
||||
self.instructionsArray = JSON.parse(data['_body']);
|
||||
self.updateInstructions();
|
||||
},
|
||||
err => console.error(err)
|
||||
);
|
||||
}
|
||||
|
||||
// load json file for commands and push each object to commands
|
||||
getCommands() {
|
||||
loadCommands() {
|
||||
let self = this;
|
||||
this.http.get('app/responses/commands.json')
|
||||
.subscribe(data => {
|
||||
let result = JSON.parse(data['_body']);
|
||||
this.commands = result
|
||||
let commands = JSON.parse(data['_body']);
|
||||
// generate regular expression for each entry
|
||||
for (var i = 0; i < commands.length; i++) {
|
||||
// preps a string for use in regular expressions
|
||||
// @see http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
|
||||
let cmd = commands[i].command.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
cmd = cmd.replace(/ /g,"\\s*")
|
||||
cmd = cmd.replace(/\n/g,"\\s*")
|
||||
let pattern = '^\\s*'+cmd+'\\s*$'
|
||||
commands[i].regex = new RegExp(pattern, 'im')
|
||||
}
|
||||
this.commands = commands
|
||||
},
|
||||
err => console.error(err)
|
||||
);
|
||||
|
|
|
@ -1 +1,206 @@
|
|||
{"inspec":{"key":"inspec.txt","extra":false},"inspec help":{"key":"inspec_help.txt","extra":false},"inspec help version":{"key":"inspec_help_version.txt","extra":false},"inspec help detect":{"key":"inspec_help_detect.txt","extra":false},"inspec help exec":{"key":"inspec_help_exec.txt","extra":false},"inspec check examples/profile":{"key":"inspec_check_examples_profile.txt","extra":false},"inspec exec examples/profile":{"key":"inspec_exec_examples_profile.txt","extra":false},"inspec exec examples/profile -t ssh://bob@host.node -i bob.rsa":{"key":"inspec_exec_examples_profile_t_ssh_bob_host_node_i_bob_rsa.txt","extra":false},"inspec exec examples/profile -b ssh --host host.node --user bob -i bob.rsa":{"key":"inspec_exec_examples_profile_b_ssh_host_host_node_user_bob_i_bob_rsa.txt","extra":false},"inspec exec examples/profile -t winrm://alice:pass@windows.node":{"key":"inspec_exec_examples_profile_t_winrm_alice_pass_windows_node.txt","extra":false},"inspec exec examples/profile -t winrm://alice:pass@windows.node --ssl --self-signed":{"key":"inspec_exec_examples_profile_t_winrm_alice_pass_windows_node_ssl_self_signed.txt","extra":false},"inspec exec examples/profile -t docker://abcdef123":{"key":"inspec_exec_examples_profile_t_docker_abcdef123.txt","extra":false},"inspec detect":{"key":"inspec_detect.txt","extra":false},"inspec detect -t ssh://bob@host.node -i bob.rsa":{"key":"inspec_detect_t_ssh_bob_host_node_i_bob_rsa.txt","extra":false},"inspec shell -c 'os.params'":{"key":"inspec_shell_c_os_params_.txt","extra":false},"inspec shell -c 'sshd_config.Protocol'":{"key":"inspec_shell_c_sshd_config_Protocol_.txt","extra":false},"inspec shell -c 'sshd_config.Protocol' -t ssh://bob@host.node -i bob.rsa":{"key":"inspec_shell_c_sshd_config_Protocol_t_ssh_bob_host_node_i_bob_rsa.txt","extra":false},"inspec shell -c 'os.params' -t docker://abcdef123":{"key":"inspec_shell_c_os_params_t_docker_abcdef123.txt","extra":false},"ls":{"key":"ls.txt","extra":true},"cat README.md":{"key":"cat_README_md.txt","extra":true},"inspec help supermarket":{"key":"inspec_help_supermarket.txt","extra":true},"inspec help compliance":{"key":"inspec_help_compliance.txt","extra":true},"inspec version":{"key":"inspec_version.txt","extra":true},"inspec detect --format json":{"key":"inspec_detect_format_json.txt","extra":true},"inspec exec examples/profile --format json":{"key":"inspec_exec_examples_profile_format_json.txt","extra":true},"inspec exec examples/profile --format json | jq":{"key":"inspec_exec_examples_profile_format_json_jq.txt","extra":true},"inspec json examples/profile":{"key":"inspec_json_examples_profile.txt","extra":true},"inspec archive examples/profile":{"key":"inspec_archive_examples_profile.txt","extra":true},"inspec env":{"key":"inspec_env.txt","extra":true},"inspec exec examples/inheritance":{"key":"inspec_exec_examples_inheritance.txt","extra":true},"inspec exec test/unit/mock/profiles/failures":{"key":"inspec_exec_test_unit_mock_profiles_failures.txt","extra":true},"echo help | inspec shell":{"key":"echo_help_inspec_shell.txt","extra":false},"echo help\\ resources | inspec shell":{"key":"echo_help_resources_inspec_shell.txt","extra":false},"echo help\\ file | inspec shell":{"key":"echo_help_file_inspec_shell.txt","extra":false},"echo help\\ command | inspec shell":{"key":"echo_help_command_inspec_shell.txt","extra":false},"echo help\\ os | inspec shell":{"key":"echo_help_os_inspec_shell.txt","extra":false},"echo command\\(\\'uname\\ -a\\'\\).stdout | inspec shell":{"key":"echo_command_uname_a_stdout_inspec_shell.txt","extra":false},"echo file\\(\\'/proc/cpuinfo\\'\\).owner | inspec shell":{"key":"echo_file_proc_cpuinfo_owner_inspec_shell.txt","extra":false},"echo sshd_config.params | inspec shell":{"key":"echo_sshd_config_params_inspec_shell.txt","extra":false},"echo describe\\ file\\(\\'/root\\'\\)\\ do'\n'\\ \\ it\\ \\{\\ should\\ exist\\ \\}'\n'\\ \\ its\\(\\'mode\\'\\)\\ \\{\\ should\\ cmp\\ \\'0750\\'\\}'\n'end | inspec shell":{"key":"echo_describe_file_root_do_it_should_exist_its_mode_should_cmp_0750_end_inspec_shell.txt","extra":false},"echo control\\ \\\"id\\\"\\ do'\n'\\ \\ title\\ \\\"Check\\ permissions\\ on\\ /root\\!\\\"'\n'\\ \\ impact\\ 0.5'\n'\\ \\ describe\\ file\\(\\'/root\\'\\)\\ do'\n'\\ \\ \\ \\ its\\(\\'mode\\'\\)\\ \\{\\ should\\ cmp\\ \\'0750\\'\\}'\n'\\ \\ end'\n'end | inspec shell":{"key":"echo_control_id_do_title_Check_permissions_on_root_impact_0_5_describe_file_root_do_its_mode_should_cmp_0750_end_end_inspec_shell.txt","extra":false}}
|
||||
{
|
||||
"inspec": {
|
||||
"key": "inspec.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec help": {
|
||||
"key": "inspec_help.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec help version": {
|
||||
"key": "inspec_help_version.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec help detect": {
|
||||
"key": "inspec_help_detect.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec help exec": {
|
||||
"key": "inspec_help_exec.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec check examples/profile": {
|
||||
"key": "inspec_check_examples_profile.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile": {
|
||||
"key": "inspec_exec_examples_profile.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile -t ssh://bob@host.node -i bob.rsa": {
|
||||
"key": "inspec_exec_examples_profile_t_ssh_bob_host_node_i_bob_rsa.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile -b ssh --host host.node --user bob -i bob.rsa": {
|
||||
"key": "inspec_exec_examples_profile_b_ssh_host_host_node_user_bob_i_bob_rsa.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile -t winrm://alice:pass@windows.node": {
|
||||
"key": "inspec_exec_examples_profile_t_winrm_alice_pass_windows_node.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile -t winrm://alice:pass@windows.node --ssl --self-signed": {
|
||||
"key": "inspec_exec_examples_profile_t_winrm_alice_pass_windows_node_ssl_self_signed.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile -t docker://abcdef123": {
|
||||
"key": "inspec_exec_examples_profile_t_docker_abcdef123.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec detect": {
|
||||
"key": "inspec_detect.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec detect -t ssh://bob@host.node -i bob.rsa": {
|
||||
"key": "inspec_detect_t_ssh_bob_host_node_i_bob_rsa.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec shell -c 'os.params'": {
|
||||
"key": "inspec_shell_c_os_params_.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec shell -c 'sshd_config.Protocol'": {
|
||||
"key": "inspec_shell_c_sshd_config_Protocol_.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec shell -c 'sshd_config.Protocol' -t ssh://bob@host.node -i bob.rsa": {
|
||||
"key": "inspec_shell_c_sshd_config_Protocol_t_ssh_bob_host_node_i_bob_rsa.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec shell -c 'os.params' -t docker://abcdef123": {
|
||||
"key": "inspec_shell_c_os_params_t_docker_abcdef123.txt",
|
||||
"extra": false,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"ls": {
|
||||
"key": "ls.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"cat README.md": {
|
||||
"key": "cat_README_md.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec help supermarket": {
|
||||
"key": "inspec_help_supermarket.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec help compliance": {
|
||||
"key": "inspec_help_compliance.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec version": {
|
||||
"key": "inspec_version.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec detect --format json": {
|
||||
"key": "inspec_detect_format_json.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile --format json": {
|
||||
"key": "inspec_exec_examples_profile_format_json.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/profile --format json | jq": {
|
||||
"key": "inspec_exec_examples_profile_format_json_jq.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec json examples/profile": {
|
||||
"key": "inspec_json_examples_profile.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec archive examples/profile": {
|
||||
"key": "inspec_archive_examples_profile.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec env": {
|
||||
"key": "inspec_env.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"inspec exec examples/inheritance": {
|
||||
"key": "inspec_exec_examples_inheritance.txt",
|
||||
"extra": true
|
||||
},
|
||||
"inspec exec test/unit/mock/profiles/failures": {
|
||||
"key": "inspec_exec_test_unit_mock_profiles_failures.txt",
|
||||
"extra": true,
|
||||
"shell" : "sh"
|
||||
},
|
||||
"help": {
|
||||
"key": "echo_help_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"help resources": {
|
||||
"key": "echo_help_resources_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"help file": {
|
||||
"key": "echo_help_file_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"help command": {
|
||||
"key": "echo_help_command_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"help os": {
|
||||
"key": "echo_help_os_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"command('uname -a').stdout": {
|
||||
"key": "echo_command_uname_a_stdout_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"file('/proc/cpuinfo').owner": {
|
||||
"key": "echo_file_proc_cpuinfo_owner_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"sshd_config.params": {
|
||||
"key": "echo_sshd_config_params_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"describe\\ file\\(\\'/root\\'\\)\\ do'\n'\\ \\ it\\ \\{\\ should\\ exist\\ \\}'\n'\\ \\ its\\(\\'mode\\'\\)\\ \\{\\ should\\ cmp\\ \\'0750\\'\\}'\n'end": {
|
||||
"key": "echo_describe_file_root_do_it_should_exist_its_mode_should_cmp_0750_end_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
},
|
||||
"control\\ \\\"id\\\"\\ do'\n'\\ \\ title\\ \\\"Check\\ permissions\\ on\\ /root\\!\\\"'\n'\\ \\ impact\\ 0.5'\n'\\ \\ describe\\ file\\(\\'/root\\'\\)\\ do'\n'\\ \\ \\ \\ its\\(\\'mode\\'\\)\\ \\{\\ should\\ cmp\\ \\'0750\\'\\}'\n'\\ \\ end'\n'end": {
|
||||
"key": "echo_control_id_do_title_Check_permissions_on_root_impact_0_5_describe_file_root_do_its_mode_should_cmp_0750_end_end_inspec_shell.txt",
|
||||
"extra": false,
|
||||
"shell" : "inspec"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
[0;32minspec> [0mcommand('uname -a').stdout
|
||||
Welcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[0;32minspec> [0m=> [31m[1;31m"[0m[31mLinux 2e958a7a89d6 4.4.19-moby #1 SMP Mon Aug 22 23:30:19 UTC 2016 x86_64 Linux[1;35m\n[0m[31m[1;31m"[0m[31m[0m
|
||||
[0G[0;32minspec> [0m=> [31m[1;31m"[0m[31mLinux 2e958a7a89d6 4.4.19-moby #1 SMP Mon Aug 22 23:30:19 UTC 2016 x86_64 Linux[1;35m\n[0m[31m[1;31m"[0m[31m[0m
|
||||
|
|
|
@ -5,13 +5,10 @@
|
|||
[0;32minspec> [0m its('mode') { should cmp '0750'}
|
||||
[0;32minspec> [0m end
|
||||
[0;32minspec> [0m end
|
||||
Welcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[0;32minspec> [0m[31m ✖ id: File /root mode should cmp "0750" (
|
||||
expected: "0750"
|
||||
got: 0700
|
||||
|
||||
|
||||
(compared using `cmp` matcher)
|
||||
)[0m
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
[0;32minspec> [0m it { should exist }
|
||||
[0;32minspec> [0m its('mode') { should cmp '0750'}
|
||||
[0;32minspec> [0m end
|
||||
Welcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[0;32minspec> [0m
|
||||
File /root
|
||||
[32m ✔ should exist[0m
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
[0;32minspec> [0mfile('/proc/cpuinfo').owner
|
||||
Welcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[0;32minspec> [0m=> [31m[1;31m"[0m[31mroot[1;31m"[0m[31m[0m
|
||||
[0G[0;32minspec> [0m=> [31m[1;31m"[0m[31mroot[1;31m"[0m[31m[0m
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
[0;32minspec> [0mhelp command
|
||||
[0;32minspec> [0mWelcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[1mName:[0m command
|
||||
|
||||
[1mDescription:[0m
|
||||
|
@ -19,9 +15,8 @@ end
|
|||
describe command('ls') do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
|
||||
|
||||
[1mWeb Reference:[0m
|
||||
|
||||
https://github.com/chef/inspec/blob/master/docs/resources.rst#command
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
[0;32minspec> [0mhelp file
|
||||
[0;32minspec> [0mWelcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[1mName:[0m file
|
||||
|
||||
[1mDescription:[0m
|
||||
|
@ -18,9 +14,8 @@ describe file('path') do
|
|||
it { should be_owned_by 'root' }
|
||||
its('mode') { should cmp '0644' }
|
||||
end
|
||||
|
||||
|
||||
|
||||
[1mWeb Reference:[0m
|
||||
|
||||
https://github.com/chef/inspec/blob/master/docs/resources.rst#file
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
[0;32minspec> [0mhelp
|
||||
[0;32minspec> [0mWelcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G
|
||||
Available commands:
|
||||
|
||||
|
@ -20,4 +16,3 @@ You are currently running on:
|
|||
OS platform: [1malpine[0m
|
||||
OS family: [1malpine[0m
|
||||
OS release: [1m3.4.0[0m
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
[0;32minspec> [0mhelp os
|
||||
[0;32minspec> [0mWelcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[1mName:[0m os
|
||||
|
||||
[1mDescription:[0m
|
||||
|
@ -19,9 +15,8 @@ end
|
|||
describe os.linux? do
|
||||
it { should eq true }
|
||||
end
|
||||
|
||||
|
||||
|
||||
[1mWeb Reference:[0m
|
||||
|
||||
https://github.com/chef/inspec/blob/master/docs/resources.rst#os
|
||||
|
||||
|
|
|
@ -1,5 +1 @@
|
|||
[0;32minspec> [0mhelp resources
|
||||
[0;32minspec> [0mWelcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0Gapache apache_conf apt ppa audit_policy auditd_conf auditd_rules command bash file bond bridge directory etc_group gem group grub_conf host iis_site inetd_conf interface iptables json kernel_module kernel_parameter linux_kernel_parameter limits_conf login_defs mount mssql_session mysql mysql_conf mysql_session npm ntp_conf oneget os os_env package parse_config parse_config_file passwd pip port postgres postgres_conf postgres_session powershell script processes registry_key windows_registry_key security_policy service systemd_service upstart_service sysv_service bsd_service launchd_service runit_service shadow ssl ssh_config sshd_config sys_info users user vbscript windows_feature xinetd_conf wmi yum yumrepo yaml csv ini
|
||||
apache apache_conf apt ppa audit_policy auditd_conf auditd_rules command bash file bond bridge directory etc_group gem group grub_conf host iis_site inetd_conf interface iptables json kernel_module kernel_parameter linux_kernel_parameter limits_conf login_defs mount mssql_session mysql mysql_conf mysql_session npm ntp_conf oneget os os_env package parse_config parse_config_file passwd pip port postgres postgres_conf postgres_session powershell script processes registry_key windows_registry_key security_policy service systemd_service upstart_service sysv_service bsd_service launchd_service runit_service shadow ssl ssh_config sshd_config sys_info users user vbscript windows_feature xinetd_conf wmi yum yumrepo yaml csv ini
|
||||
|
|
|
@ -1,6 +1,2 @@
|
|||
[0;32minspec> [0msshd_config.params
|
||||
Welcome to the interactive InSpec Shell
|
||||
To find out how to use it, type: [1mhelp[0m
|
||||
|
||||
[0G[0;32minspec> [0m=> {[31m[1;31m"[0m[31mauthorizedkeysfile[1;31m"[0m[31m[0m=>[[31m[1;31m"[0m[31m.ssh/authorized_keys[1;31m"[0m[31m[0m],
|
||||
[31m[1;31m"[0m[31msubsystem[1;31m"[0m[31m[0m=>[[31m[1;31m"[0m[31msftp[1;35m\t[0m[31m/usr/lib/ssh/sftp-server[1;31m"[0m[31m[0m]}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#terminal-container {
|
||||
max-width: 1200px;
|
||||
height: 350px;
|
||||
margin: 0 auto;
|
||||
padding: 2px;
|
||||
font-size: 16px;
|
||||
max-width: 1000px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: monospace;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
#terminal-container .terminal {
|
||||
|
@ -26,4 +28,4 @@ so we're deep assigning here to make sure that 0 is marked as black */
|
|||
on the black background so we're deep assigning to make 6 a lightskyblu*/
|
||||
:host /deep/ .xterm-color-6 {
|
||||
color: lightskyblue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
<div (keyup)="onKey($event)" (keydown)="handleDelete($event)" id="terminal-container"></div>
|
||||
<div id="terminal-container" (window:resize)="onResize($event)"></div>
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter, SimpleChange } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnChanges,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
SimpleChange
|
||||
} from '@angular/core';
|
||||
declare var Terminal: any;
|
||||
|
||||
@Component({
|
||||
|
@ -6,19 +14,22 @@ declare var Terminal: any;
|
|||
templateUrl: 'app/xterm-terminal/xterm-terminal.component.html',
|
||||
styleUrls: ['app/xterm-terminal/xterm-terminal.component.css']
|
||||
})
|
||||
export class XtermTerminalComponent implements OnInit {
|
||||
@Input() response: string;
|
||||
@Input() shell: string;
|
||||
@Output() command: EventEmitter<string> = new EventEmitter<string>();
|
||||
export class XtermTerminalComponent implements OnInit, OnChanges{
|
||||
|
||||
// handle up/down arrow functionality
|
||||
previousCommands: any = [];
|
||||
poppedCommands: any = [];
|
||||
// This Xterm implementation emits all commands, once the users presses 'Enter'
|
||||
@Output() stdin: EventEmitter<string> = new EventEmitter<string>();
|
||||
// waits for events that should be printed on the terminal
|
||||
@Input() stdout: EventEmitter<string>;
|
||||
// sets the shell prompt
|
||||
@Input() prompt: string;
|
||||
|
||||
// handle up/down command history functionality
|
||||
history: any = [];
|
||||
historyIndex: number;
|
||||
last: number;
|
||||
|
||||
// handle user input
|
||||
buffer: string = ''; // the buffer is our string of user input
|
||||
blockCmd: string = ''; // handle describe and control blocks
|
||||
// caches stdin, until the user presses Enter
|
||||
buffer: string = '';
|
||||
|
||||
// xterm variables
|
||||
terminalContainer: any;
|
||||
|
@ -27,200 +38,184 @@ export class XtermTerminalComponent implements OnInit {
|
|||
cols: string;
|
||||
rows: string;
|
||||
|
||||
// we cannot use \33[2K\r since strict mode recognize it as ocal number
|
||||
clearLine = "\u001b[2K\r"
|
||||
|
||||
ngOnInit() {
|
||||
// set up Xterm terminal (https://github.com/sourcelair/xterm.js)
|
||||
this.terminalContainer = document.getElementById('terminal-container');
|
||||
this.cols = '70';
|
||||
this.rows = '70';
|
||||
this.createTerminal();
|
||||
}
|
||||
this.initializeTerminal();
|
||||
}
|
||||
|
||||
// watch for changes on the value of response and call printResponse
|
||||
ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
|
||||
if (changes['response'] && this.term) {
|
||||
let currentResponse = changes['response'].currentValue;
|
||||
this.printResponse(currentResponse);
|
||||
ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
|
||||
let log: string[] = [];
|
||||
for (let propName in changes) {
|
||||
let changedProp = changes[propName];
|
||||
// reprint prompt
|
||||
if (propName == 'prompt' && this.term != null) {
|
||||
this.term.write(this.clearLine+this.prompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onResize(event) {
|
||||
this.updateTerminalSize()
|
||||
}
|
||||
|
||||
updateTerminalSize() {
|
||||
// recalculate cols and rows
|
||||
let charwidth = 24;
|
||||
// read size from parent wrapping element
|
||||
let cols = 80
|
||||
let rows = Math.floor(this.terminalContainer.parentElement.clientHeight / charwidth)
|
||||
this.term.resize(cols, rows);
|
||||
}
|
||||
|
||||
// create Xterm terminal (https://github.com/sourcelair/xterm.js)
|
||||
createTerminal() {
|
||||
initializeTerminal() {
|
||||
while (this.terminalContainer.children.length) {
|
||||
this.terminalContainer.removeChild(this.terminalContainer.children[0]);
|
||||
}
|
||||
this.term = new Terminal({
|
||||
cursorBlink: true
|
||||
cursorBlink: true,
|
||||
});
|
||||
|
||||
this.term.open(this.terminalContainer);
|
||||
this.term.fit();
|
||||
|
||||
var initialGeometry = this.term.proposeGeometry(),
|
||||
cols = initialGeometry.cols,
|
||||
rows = initialGeometry.rows;
|
||||
|
||||
this.cols = cols;
|
||||
this.rows = rows;
|
||||
this.runFakeTerminal()
|
||||
}
|
||||
|
||||
// more Xterm terminal stuff. we're faking it. faking it can be a good thing ;)
|
||||
runFakeTerminal() {
|
||||
// start registering the event listener, only need to be done the first time
|
||||
if (this.term._initialized) {
|
||||
return;
|
||||
}
|
||||
this.term._initialized = true;
|
||||
this.setPrompt();
|
||||
this.writePrompt();
|
||||
|
||||
// determine rows for terminal
|
||||
this.updateTerminalSize()
|
||||
|
||||
let self = this
|
||||
// raw data
|
||||
this.term.on('data', function(data){
|
||||
self.writeBuffer(data)
|
||||
})
|
||||
|
||||
// overwrite keydown handler to be able to customize cursor handling
|
||||
this.term.attachCustomKeydownHandler(function(ev){
|
||||
return self.keydown(ev);
|
||||
})
|
||||
|
||||
// handle stream events from the app
|
||||
this.stdout.subscribe(function(data) {
|
||||
self.onStdout(data)
|
||||
}, function(err) {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
// if the response matches the special message we send when the user
|
||||
// has entered 'next' or 'prev' (to navigate through the demo)
|
||||
// then no need to print the response, just set the prompt. otherwise,
|
||||
// print response and set prompt
|
||||
printResponse(response) {
|
||||
if (response.match(/.30mnext|.30mprev|.30mlast/)) {
|
||||
// call createTerminal to clear terminal screen on next and prev
|
||||
this.createTerminal();
|
||||
} else {
|
||||
this.term.writeln(response);
|
||||
this.setPrompt();
|
||||
}
|
||||
// this writes the raw buffer and includes invisible charaters
|
||||
writeBuffer(data) {
|
||||
this.term.write(data);
|
||||
this.buffer += data;
|
||||
}
|
||||
|
||||
// the value of shell is taken as an input. the default
|
||||
// shellprompt is displayed unless shell is set to 'inspec-shell'
|
||||
setPrompt() {
|
||||
this.buffer = '';
|
||||
if (this.shell === 'inspec-shell') {
|
||||
if (this.blockCmd != '') {
|
||||
this.term.write(' [32minspec>*'); // green inspec shell prompt with * to denote multi-line command
|
||||
// reset the command buffer
|
||||
resetBuffer() {
|
||||
this.buffer = ''
|
||||
}
|
||||
|
||||
// print out stdout data
|
||||
onStdout(data) {
|
||||
let res = data
|
||||
this.term.write(res);
|
||||
}
|
||||
|
||||
// print out error
|
||||
onStderr(response) {
|
||||
this.term.writeln(response);
|
||||
}
|
||||
|
||||
// writes the command prompt, a prompt is not part of the input buffer
|
||||
writePrompt() {
|
||||
this.term.write(this.prompt);
|
||||
}
|
||||
|
||||
// stores a command in commmand history
|
||||
storeInHistory(cmd) {
|
||||
// remove cariage returns from history commands
|
||||
cmd = cmd.replace(/\r/,'')
|
||||
this.history.push(cmd);
|
||||
this.historyIndex = this.history.length
|
||||
}
|
||||
|
||||
// prints the current selected command at history index
|
||||
historyCmdtoStdout() {
|
||||
this.buffer = this.history[this.historyIndex]
|
||||
this.reprintCommand(this.buffer )
|
||||
}
|
||||
|
||||
// reprint a command in the current line
|
||||
reprintCommand(cmd){
|
||||
this.term.write(this.clearLine+this.prompt + cmd)
|
||||
}
|
||||
|
||||
// resets the terminal
|
||||
clear() {
|
||||
this.term.reset()
|
||||
this.writePrompt()
|
||||
}
|
||||
|
||||
// terminal event handler
|
||||
// be aware that we need to decouple the event loops, therefore we need to
|
||||
// use setTimeout to reliable print content on the terminal
|
||||
keydown(ev) {
|
||||
// console.log(ev);
|
||||
let self = this
|
||||
if (ev.keyCode === 13) {
|
||||
let cmd = self.buffer
|
||||
// special handling for clear and cls
|
||||
if (/^\s*clear|cls\s*/.exec(cmd) != null) {
|
||||
// reset terminal
|
||||
setTimeout(function(){
|
||||
self.clear()
|
||||
}, 0);
|
||||
} else {
|
||||
this.term.write(' [32minspec> '); // green inspec shell prompt
|
||||
}
|
||||
} else {
|
||||
this.term.write('[36m$ '); // blue regular shell prompt
|
||||
}
|
||||
}
|
||||
|
||||
// delete everything on the line
|
||||
deleteCharacters() {
|
||||
// don't delete the prompt
|
||||
let letters = this.term.x - 2;
|
||||
for (var i = 0; i < letters; i++) {
|
||||
this.term.write('\b \b');
|
||||
}
|
||||
}
|
||||
|
||||
// keydown calls handleDelete to check if the user is holding down the backpace key
|
||||
// TODO: make this work! the backspace event isn't coming in for some reason :(
|
||||
handleDelete(ev) {
|
||||
if (ev.keyCode == 8) {
|
||||
this.deleteCharacters();
|
||||
}
|
||||
}
|
||||
|
||||
// handle describe and control blocks. if the command entered matches the
|
||||
// syntax we would generally expect in a control/describe block, then we
|
||||
// wait to have the whole string command (as marked by 'end' for a simple describe
|
||||
// block and end end for a control block) and emit that
|
||||
handleBlockCommand(buffer) {
|
||||
this.blockCmd += buffer + '';
|
||||
if (this.blockCmd.match(/^control/)) {
|
||||
if (this.blockCmd.match(/end.*end.*/)) {
|
||||
this.command.emit(this.blockCmd);
|
||||
this.blockCmd = '';
|
||||
} else {
|
||||
this.setPrompt();
|
||||
}
|
||||
} else {
|
||||
if (buffer.match(/^end$/)) {
|
||||
this.command.emit(this.blockCmd);
|
||||
this.blockCmd = '';
|
||||
} else {
|
||||
this.setPrompt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// onKey will check if the character is printable (on keyup) and add it to the buffer. if the enter key is hit
|
||||
// the value of the buffer is emitted as 'command'. onKey also supports proper backspace handling and
|
||||
// the ability to up-arrow through previous commands/down arrow back through them.
|
||||
onKey(ev) {
|
||||
var shell = null
|
||||
// determine if the character is a printable one
|
||||
var printable = ['Alt', 'Control', 'Meta', 'Shift', 'CapsLock', 'Tab', 'Escape', 'ArrowLeft', 'ArrowRight'].indexOf(ev.key) == -1
|
||||
|
||||
// on enter, save command to array and send current value of buffer
|
||||
// to parent component (app)
|
||||
if (ev.keyCode == 13) {
|
||||
if ((this.buffer === 'clear') || (this.buffer === 'cls')) {
|
||||
this.createTerminal();
|
||||
} else {
|
||||
this.previousCommands.push(this.buffer);
|
||||
this.term.write('\r\n');
|
||||
if (this.shell === 'inspec-shell') {
|
||||
// if the command entered matches any of the typical describe and
|
||||
// control blocks, then we call handleBlockCommand to not emit the
|
||||
// value until we have the whole multi-line command
|
||||
if (this.buffer.match(/^describe.*|^it.*|^end$|^impact.*|^title.*|^control.*/)) {
|
||||
this.handleBlockCommand(this.buffer);
|
||||
} else {this.command.emit(this.buffer);}
|
||||
} else {
|
||||
this.command.emit(this.buffer);
|
||||
}
|
||||
// decouple from event loop, write a new line
|
||||
setTimeout(function(){
|
||||
self.term.writeln('')
|
||||
self.stdin.emit(cmd);
|
||||
self.storeInHistory(cmd)
|
||||
}, 0);
|
||||
}
|
||||
// reset index to last item +1
|
||||
self.resetBuffer()
|
||||
}
|
||||
// on backspace, pop characters from buffer
|
||||
else if (ev.keyCode == 8) {
|
||||
// if inspec shell is being used, this needs to be set to 9 to account for the extra letters
|
||||
if (this.shell === 'inspec-shell') {
|
||||
if (this.term.x > 8) {
|
||||
this.buffer = this.buffer.substr(0, this.buffer.length-1);
|
||||
this.term.write('\b \b');
|
||||
}
|
||||
} else {
|
||||
// setting the value here to 3 ensures that we don't delete the promp '$' or the space after it
|
||||
if (this.term.x > 2) {
|
||||
this.buffer = this.buffer.substr(0, this.buffer.length-1);
|
||||
this.term.write('\b \b');
|
||||
}
|
||||
}
|
||||
self.buffer = self.buffer.substr(0, self.buffer.length-1);
|
||||
setTimeout(function(){
|
||||
self.reprintCommand(self.buffer)
|
||||
}, 0);
|
||||
}
|
||||
// on up arrow, delete anything on line, print previous command
|
||||
// and push last to poppedCommands
|
||||
else if (ev.keyCode === 38) {
|
||||
let last;
|
||||
this.buffer = '';
|
||||
if (this.previousCommands.length > 0) {
|
||||
last = this.previousCommands.pop();
|
||||
this.poppedCommands.push(last);
|
||||
} else {
|
||||
last = '';
|
||||
}
|
||||
this.deleteCharacters();
|
||||
this.buffer = last;
|
||||
this.term.write(last);
|
||||
}
|
||||
// on down arrow, delete anything on line, print last item from poppedCommands
|
||||
// and push previous to previousCommands
|
||||
// press the up key, find previous item in cmd history
|
||||
else if (ev.keyCode === 40) {
|
||||
let previous;
|
||||
this.buffer = '';
|
||||
if (this.poppedCommands.length > 0) {
|
||||
previous = this.poppedCommands.pop();
|
||||
this.previousCommands.push(previous);
|
||||
} else {
|
||||
previous = '';
|
||||
}
|
||||
this.deleteCharacters();
|
||||
this.buffer = previous;
|
||||
this.term.write(previous);
|
||||
setTimeout(function(){
|
||||
if (self.historyIndex < self.history.length - 1) {
|
||||
self.historyIndex += 1
|
||||
self.historyCmdtoStdout()
|
||||
}
|
||||
}, 0);
|
||||
|
||||
}
|
||||
// if the character is printable and none of the event key codes from above were matched
|
||||
// then we write the character on the prompt line and add the character to the buffer
|
||||
else if (printable) {
|
||||
this.term.write(ev.key);
|
||||
this.buffer += ev.key;
|
||||
// press the down key, find next item in cmd history
|
||||
else if (ev.keyCode === 38) {
|
||||
setTimeout(function(){
|
||||
if (self.historyIndex >= 1) {
|
||||
self.historyIndex -= 1
|
||||
self.historyCmdtoStdout()
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
// this forces xterm not to handle the key events with its own event handler
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ gulp.task('copy', function(){
|
|||
'app/**/*.css',
|
||||
'app/**/*.html',
|
||||
'tutorial_files/**',
|
||||
'node_modules/shellwords/src/shellwords.coffee',
|
||||
])
|
||||
.pipe(gcopy('dist/'));
|
||||
})
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>InSpec Demo</title>
|
||||
<title>InSpec Interactive Tutorial</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- build:css css/combined.css -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="node_modules/xterm/src/xterm.css">
|
||||
<link rel="stylesheet" href="assets/font-awesome/css/font-awesome.css">
|
||||
<!-- endbuild -->
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<!-- FIXME I need compilation and integration vv -->
|
||||
<script src="node_modules/shellwords/src/shellwords.coffee"></script>
|
||||
|
||||
<!-- build:js scripts/combined.js -->
|
||||
<script src="node_modules/xterm/src/xterm.js"></script>
|
||||
<script src="node_modules/xterm/addons/fit/fit.js"></script>
|
||||
|
|
BIN
www/tutorial/inspec-logo-white.png
Normal file
BIN
www/tutorial/inspec-logo-white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.9 KiB |
|
@ -23,7 +23,6 @@
|
|||
"core-js": "^2.4.0",
|
||||
"reflect-metadata": "^0.1.3",
|
||||
"rxjs": "5.0.0-beta.6",
|
||||
"shellwords": "^0.1.0",
|
||||
"systemjs": "0.19.27",
|
||||
"webpack": "^1.13.2",
|
||||
"webpack-dev-server": "^1.15.1",
|
||||
|
|
Loading…
Reference in a new issue