318 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <q-dialog
 | |
|     ref="dialogRef"
 | |
|     @hide="onDialogHide"
 | |
|     persistent
 | |
|     @keydown.esc="onDialogHide"
 | |
|     :maximized="maximized"
 | |
|   >
 | |
|     <q-card class="dialog-plugin" style="min-width: 60vw">
 | |
|       <q-bar>
 | |
|         Run a script on {{ agent.hostname }}
 | |
|         <q-space />
 | |
|         <q-btn
 | |
|           dense
 | |
|           flat
 | |
|           icon="minimize"
 | |
|           @click="maximized = false"
 | |
|           :disable="!maximized"
 | |
|         >
 | |
|           <q-tooltip v-if="maximized" class="bg-white text-primary"
 | |
|             >Minimize</q-tooltip
 | |
|           >
 | |
|         </q-btn>
 | |
|         <q-btn
 | |
|           dense
 | |
|           flat
 | |
|           icon="crop_square"
 | |
|           @click="maximized = true"
 | |
|           :disable="maximized"
 | |
|         >
 | |
|           <q-tooltip v-if="!maximized" class="bg-white text-primary"
 | |
|             >Maximize</q-tooltip
 | |
|           >
 | |
|         </q-btn>
 | |
|         <q-btn dense flat icon="close" v-close-popup>
 | |
|           <q-tooltip class="bg-white text-primary">Close</q-tooltip>
 | |
|         </q-btn>
 | |
|       </q-bar>
 | |
|       <q-form @submit.prevent="sendScript">
 | |
|         <q-card-section>
 | |
|           <tactical-dropdown
 | |
|             :rules="[(val) => !!val || '*Required']"
 | |
|             v-model="state.script"
 | |
|             :options="filteredScriptOptions"
 | |
|             label="Select script"
 | |
|             outlined
 | |
|             mapOptions
 | |
|             filterable
 | |
|           >
 | |
|             <template v-slot:after>
 | |
|               <q-btn
 | |
|                 size="sm"
 | |
|                 round
 | |
|                 dense
 | |
|                 flat
 | |
|                 icon="info"
 | |
|                 @click="openScriptURL"
 | |
|               >
 | |
|                 <q-tooltip
 | |
|                   v-if="syntax"
 | |
|                   class="bg-white text-primary text-body1"
 | |
|                   v-html="formatScriptSyntax(syntax)"
 | |
|                 />
 | |
|               </q-btn>
 | |
|             </template>
 | |
|           </tactical-dropdown>
 | |
|         </q-card-section>
 | |
|         <q-card-section>
 | |
|           <tactical-dropdown
 | |
|             v-model="state.args"
 | |
|             label="Script Arguments (press Enter after typing each argument)"
 | |
|             filled
 | |
|             use-input
 | |
|             multiple
 | |
|             hide-dropdown-icon
 | |
|             input-debounce="0"
 | |
|             new-value-mode="add"
 | |
|           />
 | |
|         </q-card-section>
 | |
|         <q-card-section>
 | |
|           <tactical-dropdown
 | |
|             v-model="state.env_vars"
 | |
|             :label="envVarsLabel"
 | |
|             filled
 | |
|             use-input
 | |
|             multiple
 | |
|             hide-dropdown-icon
 | |
|             input-debounce="0"
 | |
|             new-value-mode="add"
 | |
|           />
 | |
|         </q-card-section>
 | |
|         <q-card-section>
 | |
|           <q-option-group
 | |
|             v-model="state.output"
 | |
|             :options="outputOptions"
 | |
|             color="primary"
 | |
|             inline
 | |
|             dense
 | |
|           />
 | |
|         </q-card-section>
 | |
|         <q-card-section v-if="state.output === 'email'">
 | |
|           <div class="q-gutter-sm">
 | |
|             <q-radio
 | |
|               dense
 | |
|               v-model="state.emailMode"
 | |
|               val="default"
 | |
|               label="Use email addresses from global settings"
 | |
|             />
 | |
|             <q-radio
 | |
|               dense
 | |
|               v-model="state.emailMode"
 | |
|               val="custom"
 | |
|               label="Custom emails"
 | |
|             />
 | |
|           </div>
 | |
|         </q-card-section>
 | |
|         <q-card-section
 | |
|           v-if="state.emailMode === 'custom' && state.output === 'email'"
 | |
|         >
 | |
|           <tactical-dropdown
 | |
|             v-model="state.emails"
 | |
|             label="Email recipients (press Enter after typing each email)"
 | |
|             filled
 | |
|             use-input
 | |
|             multiple
 | |
|             hide-dropdown-icon
 | |
|             input-debounce="0"
 | |
|             new-value-mode="add"
 | |
|           />
 | |
|         </q-card-section>
 | |
|         <q-card-section v-if="state.output === 'collector'">
 | |
|           <tactical-dropdown
 | |
|             :rules="[(val) => !!val || '*Required']"
 | |
|             outlined
 | |
|             v-model="state.custom_field"
 | |
|             :options="customFieldOptions"
 | |
|             label="Select custom field"
 | |
|             mapOptions
 | |
|             filterable
 | |
|           />
 | |
|           <q-checkbox v-model="state.save_all_output" label="Save all output" />
 | |
|         </q-card-section>
 | |
|         <q-card-section v-if="agent.plat === 'windows'">
 | |
|           <q-checkbox v-model="state.run_as_user" label="Run As User">
 | |
|             <q-tooltip>{{ runAsUserToolTip }}</q-tooltip>
 | |
|           </q-checkbox>
 | |
|         </q-card-section>
 | |
|         <q-card-section>
 | |
|           <q-input
 | |
|             v-model.number="state.timeout"
 | |
|             dense
 | |
|             outlined
 | |
|             type="number"
 | |
|             style="max-width: 150px"
 | |
|             label="Timeout (seconds)"
 | |
|             stack-label
 | |
|             :rules="[
 | |
|               (val) => !!val || '*Required',
 | |
|               (val) => val >= 5 || 'Minimum is 5 seconds',
 | |
|             ]"
 | |
|           />
 | |
|         </q-card-section>
 | |
|         <q-card-actions align="right">
 | |
|           <q-btn label="Cancel" v-close-popup />
 | |
|           <q-btn
 | |
|             :loading="loading"
 | |
|             :disabled="loading"
 | |
|             label="Run"
 | |
|             color="primary"
 | |
|             type="submit"
 | |
|           />
 | |
|         </q-card-actions>
 | |
|         <q-card-section
 | |
|           v-if="ret !== null"
 | |
|           class="q-pl-md q-pr-md q-pt-none q-ma-none scroll"
 | |
|           style="max-height: 50vh"
 | |
|         >
 | |
|           <pre>{{ ret }}</pre>
 | |
|         </q-card-section>
 | |
|       </q-form>
 | |
|     </q-card>
 | |
|   </q-dialog>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| // composition imports
 | |
| import { ref, watch, computed } from "vue";
 | |
| import { useDialogPluginComponent, openURL } from "quasar";
 | |
| import { useScriptDropdown } from "@/composables/scripts";
 | |
| import { useCustomFieldDropdown } from "@/composables/core";
 | |
| import { runScript } from "@/api/agents";
 | |
| import { notifySuccess } from "@/utils/notify";
 | |
| import { envVarsLabel, runAsUserToolTip } from "@/constants/constants";
 | |
| import {
 | |
|   formatScriptSyntax,
 | |
|   removeExtraOptionCategories,
 | |
| } from "@/utils/format";
 | |
| 
 | |
| //ui imports
 | |
| import TacticalDropdown from "@/components/ui/TacticalDropdown.vue";
 | |
| 
 | |
| // static data
 | |
| const outputOptions = [
 | |
|   { label: "Wait for Output", value: "wait" },
 | |
|   { label: "Fire and Forget", value: "forget" },
 | |
|   { label: "Email results", value: "email" },
 | |
|   { label: "Save results to Custom Field", value: "collector" },
 | |
|   { label: "Save results to Agent Notes", value: "note" },
 | |
| ];
 | |
| 
 | |
| export default {
 | |
|   name: "RunScript",
 | |
|   emits: [...useDialogPluginComponent.emits],
 | |
|   components: { TacticalDropdown },
 | |
|   props: {
 | |
|     agent: !Object,
 | |
|     script: Number,
 | |
|   },
 | |
|   setup(props) {
 | |
|     // setup quasar dialog plugin
 | |
|     const { dialogRef, onDialogHide } = useDialogPluginComponent();
 | |
| 
 | |
|     // setup dropdowns
 | |
|     const {
 | |
|       script,
 | |
|       scriptOptions,
 | |
|       defaultTimeout,
 | |
|       defaultArgs,
 | |
|       defaultEnvVars,
 | |
|       syntax,
 | |
|       link,
 | |
|     } = useScriptDropdown(props.script, {
 | |
|       onMount: true,
 | |
|       filterByPlatform: props.agent.plat,
 | |
|     });
 | |
|     const { customFieldOptions } = useCustomFieldDropdown({ onMount: true });
 | |
| 
 | |
|     // main run script functionaity
 | |
|     const state = ref({
 | |
|       output: "wait",
 | |
|       emails: [],
 | |
|       emailMode: "default",
 | |
|       custom_field: null,
 | |
|       save_all_output: false,
 | |
|       script,
 | |
|       args: defaultArgs,
 | |
|       env_vars: defaultEnvVars,
 | |
|       timeout: defaultTimeout,
 | |
|       run_as_user: false,
 | |
|     });
 | |
| 
 | |
|     const ret = ref(null);
 | |
|     const loading = ref(false);
 | |
|     const maximized = ref(false);
 | |
| 
 | |
|     async function sendScript() {
 | |
|       ret.value = null;
 | |
|       loading.value = true;
 | |
| 
 | |
|       ret.value = await runScript(props.agent.agent_id, state.value);
 | |
|       loading.value = false;
 | |
|       if (state.value.output === "forget") {
 | |
|         onDialogHide();
 | |
|         notifySuccess(ret.value);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     function openScriptURL() {
 | |
|       link.value ? openURL(link.value) : null;
 | |
|     }
 | |
| 
 | |
|     const filteredScriptOptions = computed(() => {
 | |
|       return removeExtraOptionCategories(
 | |
|         scriptOptions.value.filter(
 | |
|           (script) =>
 | |
|             script.category ||
 | |
|             !script.supported_platforms ||
 | |
|             script.supported_platforms.length === 0 ||
 | |
|             script.supported_platforms.includes(props.agent.plat)
 | |
|         )
 | |
|       );
 | |
|     });
 | |
| 
 | |
|     // watchers
 | |
|     watch(
 | |
|       [() => state.value.output, () => state.value.emailMode],
 | |
|       () => (state.value.emails = [])
 | |
|     );
 | |
| 
 | |
|     return {
 | |
|       // reactive data
 | |
|       state,
 | |
|       loading,
 | |
|       filteredScriptOptions,
 | |
|       link,
 | |
|       syntax,
 | |
|       ret,
 | |
|       maximized,
 | |
|       customFieldOptions,
 | |
| 
 | |
|       // non-reactive data
 | |
|       outputOptions,
 | |
|       runAsUserToolTip,
 | |
|       envVarsLabel,
 | |
| 
 | |
|       //methods
 | |
|       formatScriptSyntax,
 | |
|       sendScript,
 | |
|       openScriptURL,
 | |
| 
 | |
|       // quasar dialog plugin
 | |
|       dialogRef,
 | |
|       onDialogHide,
 | |
|     };
 | |
|   },
 | |
| };
 | |
| </script>
 |