-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexecutor.js
More file actions
executable file
·240 lines (225 loc) · 8.74 KB
/
Copy pathexecutor.js
File metadata and controls
executable file
·240 lines (225 loc) · 8.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/usr/bin/env node
const pjson = require('./package.json');
const os = require('os');
const path = require('path');
const fs = require('mz/fs');
const getStdin = require('get-stdin');
var Promise = require('bluebird');
const keytar = require('keytar');
const commandLineArgs = require('command-line-args');
const default_fqdn = "datacenterdev.service-now.com";
const globalScopeSysId = "515259cb6f2a51004c27511e5d3ee4fc";
const scriptName = pjson.name + " v" + pjson.version;
const USAGE_MSG = scriptName + "\n"+
"Command Usage:" +
`\n node ${path.basename(process.argv[1])} [--instance instance] `+
"[--user user] --files fileargs --file file| file [--scope sys_id]\n" +
"\n --instance, -i instance instance fqdn to access "+
`\n default:${default_fqdn})\n`+
"\n --user, -u user username to access instance as "+
"\n default: current username (`whoami`)" +
"\n --file, -f file JS file to execute as the contents of instance's Background Scripts page" +
"\n '-' : stdin"+
"\n --files, -F file \\newline delimited list of files to concatonate, and execute. Files specified by --file are appended to this list" +
"\n --scope, -s scope_sys_id sys_id of the scope you want to execute this code in, " +
`\n default:${globalScopeSysId} which is the 'global' scope`+
"\n --version, -V Print the Version number" +
"\n --help, -h Print this help message" +
"\n" +
"\nIf you have issues, make sure you're admin (Can you access /sys.scripts.do manually)" +
"\nAlso, delete the file ~/.executorCookies.json and the file ./.token" +
"\nthese are files used to speed up future accesses, and sometimes they expire and/or get corrupted";
var request = require('request-promise');
var cheerio = require('cheerio');
var FileCookieStore = require('tough-cookie-filestore');
var agentkeepalive = require('agentkeepalive'),
HttpsAgent = agentkeepalive.HttpsAgent,
agent = new HttpsAgent({keepAlive: true, keepAliveTimeout:300000});
var AllHtmlEntities = require('html-entities');
AllHtmlEntities = new AllHtmlEntities.AllHtmlEntities();
var options;
try {
options = commandLineArgs([
{name: 'instance', alias:'i', type: String, defaultValue: default_fqdn},
{name: 'user', alias:'u', type: String, defaultValue: os.userInfo().username},
{name: 'file', alias:'f', defaultOption: true, type: String,multiple:true},
{name: 'files', alias:'F', type:String},
{name: 'scope', alias:'s', type:String, defaultValue:globalScopeSysId},
{name: 'help', alias:'h', type:Boolean},
{name: 'version', alias:'V', type:Boolean}
]);
}catch(e){
console.error(USAGE_MSG);
process.exit(-1);
}
if(options.help){
console.error(USAGE_MSG);
process.exit(-1);
}
if(options.version){
console.log(scriptName);
process.exit(0);
}
if(!options.file && !options.files){
console.error(USAGE_MSG);
process.exit(-2);
}
if (!options.file){
options.file = [];
}
if(!(options.file instanceof Array)){
options.file = [options.file];
}
if(options.files){
options.file.unshift(...(
fs.readFileSync(options.files).toString()
.split("\n")
.filter(file => file.length > 0)
.filter(file => !(file.match(/^(;|#|\/\/)/)))));
}
if(! options.file){
console.error("Please specify a js file to execute");
console.error(USAGE_MSG);
process.exit(-3);
}
//noinspection JSUnresolvedVariable
const uri = "https://" + options.instance + "/";
//noinspection JSUnresolvedVariable
var password = keytar.getPassword(options.instance, options.user);
if(!password ){
console.error('Password not found in keyring,');
console.error('please create a password in the login keyring');
//noinspection JSUnresolvedVariable
console.error(`\t Name:${options.instance}`);
console.error(`\t Account:${options.user}`);
process.exit(-3);
}
const cookiePath = os.homedir() + '/.executorCookies.json';
if(!fs.existsSync(cookiePath)){
fs.writeFileSync(cookiePath,"{}");
}
const fcs = new FileCookieStore(cookiePath);
var j = request.jar(fcs);
request = request.defaults({
jar: j,
agent:agent,
gzip:true,
resolveWithFullResponse: true,
baseUrl:uri
});
function executeCode(code, token) {
if(! token){
throw Object.assign(new Error("empty token"),{statusCode:302,script:code});
}
return request.post('sys.scripts.do',{
form: {
script: code,
runscript: "Run script",
sysparm_ck: token,
sys_scope: options.scope
}})
.catch(function(e){
e.script = code;
throw e;
})
}
function getFiles() {
var files = [];
options.file.forEach(function(file) {
if(file === "-"){
files.push(Promise.resolve().then(function(){return getStdin()}).timeout(1500).catch(Promise.TimeoutError, function() {
console.warn("STDIN specified, but could not be retrieved");
}));
}else {
files.push(
fs.readFile(file,"utf8")
.catch(function () {
console.warn("File Not Found:", file);
return Promise.resolve("");
})
.then(buffer=>[file ,buffer.toString()])
)
}
});
return [
Promise.all(files).map(function(item,index,arrayLength){
var [name,code] = item;
code = JSON.stringify(code);
return `new GlideScriptEvaluator().evaluateString(${code},'${name}',false);`;
}).reduce(function(acc,item,index,arrayLength){
return acc +";\n" + item;
}).then(function(code){
// if(options.files){
// fs.writeFile("codeExecuted.js",code,'utf8')
// }
return code
}),
fs.readFile(".token")
.catch(function () {
return Promise.resolve("");
})
.then(buffer => buffer.toString())
]
}
process.argv.shift();
process.argv.shift();
Promise.resolve()
.then(getFiles)
.catch(e => console.dir(e),function (){
process.exit(-2);
})
.spread(executeCode)
.catch(e => e.statusCode == 302, function handleNewLogin(e) {
var code = e.script;
return new Promise(function(cb){
//noinspection JSUnresolvedVariable
fcs.removeCookies(options.instance, "/",cb);
})
.then(function(){return request.get("login.do")})
.then(function (incomingMessage) {
// var foo = j;
// var f = fcs;
var $ = cheerio.load(incomingMessage.body);
var form = $('#loginPage');
var token = $('#sysparm_ck', form).val();
return request.post("login.do", {
form: {
user_name: options.user,
user_password: password,
sysparm_ck: token,
sys_action: 'sysverb_login',
sysparm_login_url: 'welcome.do'
},
followAllRedirects: true
});
})
.then(function (incomingMessage) {
if(incomingMessage.body.indexOf("You are not logged in, or your session has expired. Redirecting to the login page...") != -1){
return Promise.reject("Session Expired");
}else if(incomingMessage.body.indexOf("User name or password invalid") != -1) {
return Promise.reject("Invalid Username/Password");
}else if(incomingMessage.body == "not authorized"){
return Promise.reject("Not Authorized! Need to Escalate?");
}else{
return request.get("sys.scripts.do");
}
})
.then(function (incomingMessage) {
var $ = cheerio.load(incomingMessage.body);
var token = $('[name=sysparm_ck]').val();
fs.writeFileSync(".token",token);
return Promise.resolve([code,token]);
})
.spread(executeCode)
.catch(function(e){
console.error(e);
process.exit(-4);
});
})
.then(function printResult(incomingMessage,code,token) {
var $ = cheerio.load(incomingMessage.body);
var pre = $("pre");
var html = pre.html();
console.log(AllHtmlEntities.decode(html.replace(/<br\/?>/gi, '\n')));
return Promise.resolve();
});