types#Scan TypeScript Examples
The following examples show how to use
types#Scan.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ScanForm.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
ScanForm: React.FC<{
propValues: ScanFormValues;
organizationOption: OrganizationOption[];
tags: OrganizationTag[];
global?: any;
onSubmit: (values: any) => Promise<void>;
type: 'create' | 'edit';
scan?: Scan;
scanSchema: ScanSchema;
}> = ({
propValues,
organizationOption,
tags,
global,
onSubmit,
type,
scan,
scanSchema
}) => {
const setDefault = () => ({
name: scan ? scan.name : 'censys',
arguments: scan ? scan.arguments : '{}',
frequency: scan ? scan.frequency : 1,
frequencyUnit: scan ? propValues.frequencyUnit : 'minute',
isGranular: scan ? scan.isGranular : false,
isUserModifiable: scan ? scan.isUserModifiable : false,
isSingleScan: scan ? scan.isSingleScan : false,
organizations: scan ? propValues.organizations : [],
tags: scan ? propValues.tags : []
});
const [organizationOptions, setOrganizationOptions] = useState<
OrganizationOption[]
>(organizationOption);
const [tagOptions, setTagOptions] = useState<OrganizationOption[]>([]);
const [values, setValues] = useState<ScanFormValues>(setDefault());
const [schemaUpdated, setSchemaUpdated] = useState<boolean>(false);
const onTextChange: React.ChangeEventHandler<
HTMLInputElement | HTMLSelectElement
> = (e) => {
onChange(e.target.name, e.target.value);
//Ensures global scans can't be granular
if (type === 'create' && scanSchema[e.target.value]) {
onChange('isGranular', false);
}
};
const onChange = (name: string, value: any) => {
setValues((values) => ({
...values,
[name]: value
}));
};
const setDefaultValues = useCallback(async () => {
try {
setOrganizationOptions(organizationOption);
setTagOptions(tags.map((tag) => ({ label: tag.name, value: tag.id })));
if (scanSchema && scanSchema[values.name]) {
setSchemaUpdated(true);
}
if (scan) {
setValues((values) => ({
...values,
name: scan.name,
frequency: propValues.frequency,
frequencyUnit: propValues.frequencyUnit,
isGranular: scan.isGranular,
isUserModifiable: scan.isUserModifiable,
isSingleScan: scan.isSingleScan,
organizations: propValues.organizations,
tags: propValues.tags
}));
}
} catch (e) {
console.error(e);
}
}, [
organizationOption,
propValues.frequency,
propValues.frequencyUnit,
propValues.organizations,
propValues.tags,
tags,
scan,
scanSchema,
values.name
]);
useEffect(() => {
setDefaultValues();
}, [setDefaultValues]);
return (
<Form
onSubmit={async (e) => {
e.preventDefault();
await onSubmit({
name: values.name,
arguments: values.arguments,
organizations: values.organizations,
tags: values.tags,
frequency: values.frequency,
frequencyUnit: values.frequencyUnit,
isGranular: values.isGranular,
isUserModifiable: values.isUserModifiable,
isSingleScan: values.isSingleScan
});
}}
className={classes.form}
>
{type === 'create' && scanSchema && <Label htmlFor="name">Name</Label> && (
<Dropdown
required
id="name"
name="name"
className={classes.textField}
onChange={onTextChange}
value={values.name}
>
{Object.keys(scanSchema).map((i) => {
return (
<option key={i} value={i}>
{i}
</option>
);
})}
</Dropdown>
)}
{schemaUpdated && <p>{scanSchema[values.name].description}</p>}
{/* <Label htmlFor="arguments">Arguments</Label>
<TextInput
required
id="arguments"
name="arguments"
className={classes.textField}
type="text"
value={values.arguments}
onChange={onTextChange}
/> */}
{(values.name === 'censysIpv4' ||
values.name === 'webscraper' ||
values.name === 'censysCertificates' ||
(schemaUpdated && !scanSchema[values.name].global) ||
!global) && (
<Checkbox
id="isGranular"
label="Limit enabled organizations"
name="isGranular"
checked={values.isGranular}
onChange={(e) => {
onChange('isGranular', e.target.checked);
if (!e.target.checked) {
// Only granular scans can be user-modifiable.
onChange('isUserModifiable', false);
}
}}
/>
)}
{values.isGranular && (
<>
<Label htmlFor="organizations">Enabled Organizations</Label>
<MultiSelect
name="organizations"
options={organizationOptions}
value={values.organizations}
onChange={(e) => onChange('organizations', e)}
zIndex={100}
/>
<Label htmlFor="tags">Enabled Organization Tags</Label>
<MultiSelect
name="tags"
options={tagOptions}
value={values.tags}
onChange={(e) => onChange('tags', e)}
zIndex={99}
/>
<br />
</>
)}
{values.isGranular && (
<>
<Checkbox
id="isUserModifiable"
label="Allow any organization's admins to toggle this scan on/off"
name="isUserModifiable"
checked={values.isUserModifiable}
onChange={(e) => onChange('isUserModifiable', e.target.checked)}
/>
<br />
</>
)}
<Checkbox
id="isSingleScan"
label="Run scan once"
name="isSingleScan"
checked={values.isSingleScan}
onChange={(e) => onChange('isSingleScan', e.target.checked)}
/>
{!values.isSingleScan && (
<div className="form-group form-inline">
<label style={{ marginRight: '10px' }} htmlFor="frequency">
Run every
</label>
<TextInput
id="frequency"
name="frequency"
type="number"
style={{
display: 'inline-block',
width: '150px',
marginRight: '15px'
}}
value={values.frequency}
onChange={(e) => {
onChange(e.target.name, Number(e.target.value));
}}
/>
<Dropdown
id="frequencyUnit"
name="frequencyUnit"
onChange={onTextChange}
value={values.frequencyUnit}
style={{ display: 'inline-block', width: '150px' }}
>
<option value="minute">Minute(s)</option>
<option value="hour">Hour(s)</option>
<option value="day">Day(s)</option>
</Dropdown>
</div>
)}
<br />
{type === 'edit' && (
<Link to={`/scans`}>
<Button type="button" outline>
{' '}
Return to Scans
</Button>
</Link>
)}
<Button type="submit">
{type === 'edit' ? 'Save Changes' : 'Create Scan'}
</Button>
</Form>
);
}
Example #2
Source File: Organization.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
Organization: React.FC = () => {
const {
apiGet,
apiPut,
apiPost,
user,
setFeedbackMessage
} = useAuthContext();
const { organizationId } = useParams<{ organizationId: string }>();
const [organization, setOrganization] = useState<OrganizationType>();
const [tags, setTags] = useState<AutocompleteType[]>([]);
const [userRoles, setUserRoles] = useState<Role[]>([]);
const [scanTasks, setScanTasks] = useState<ScanTask[]>([]);
const [scans, setScans] = useState<Scan[]>([]);
const [scanSchema, setScanSchema] = useState<ScanSchema>({});
const [newUserValues, setNewUserValues] = useState<{
firstName: string;
lastName: string;
email: string;
organization?: OrganizationType;
role: string;
}>({
firstName: '',
lastName: '',
email: '',
role: ''
});
const classes = useStyles();
const [tagValue, setTagValue] = React.useState<AutocompleteType | null>(null);
const [inputValue, setInputValue] = React.useState('');
const [dialog, setDialog] = React.useState<{
open: boolean;
type?: 'rootDomains' | 'ipBlocks' | 'tags';
label?: string;
}>({ open: false });
const dateAccessor = (date?: string) => {
return !date || new Date(date).getTime() === new Date(0).getTime()
? 'None'
: `${formatDistanceToNow(parseISO(date))} ago`;
};
const userRoleColumns: Column<Role>[] = [
{
Header: 'Name',
accessor: ({ user }) => user.fullName,
width: 200,
disableFilters: true,
id: 'name'
},
{
Header: 'Email',
accessor: ({ user }) => user.email,
width: 150,
minWidth: 150,
id: 'email',
disableFilters: true
},
{
Header: 'Role',
accessor: ({ approved, role, user }) => {
if (approved) {
if (user.invitePending) {
return 'Invite pending';
} else if (role === 'admin') {
return 'Administrator';
} else {
return 'Member';
}
}
return 'Pending approval';
},
width: 50,
minWidth: 50,
id: 'approved',
disableFilters: true
},
{
Header: () => {
return (
<div style={{ justifyContent: 'flex-center' }}>
<Button color="secondary" onClick={() => setDialog({ open: true })}>
<ControlPoint style={{ marginRight: '10px' }}></ControlPoint>
Add member
</Button>
</div>
);
},
id: 'action',
Cell: ({ row }: { row: { index: number } }) => {
const isApproved =
!organization?.userRoles[row.index] ||
organization?.userRoles[row.index].approved;
return (
<>
{isApproved ? (
<Button
onClick={() => {
removeUser(row.index);
}}
color="secondary"
>
<p>Remove</p>
</Button>
) : (
<Button
onClick={() => {
approveUser(row.index);
}}
color="secondary"
>
<p>Approve</p>
</Button>
)}
</>
);
},
disableFilters: true
}
];
const scanColumns: Column<Scan>[] = [
{
Header: 'Name',
accessor: 'name',
width: 150,
id: 'name',
disableFilters: true
},
{
Header: 'Description',
accessor: ({ name }) => scanSchema[name] && scanSchema[name].description,
width: 200,
minWidth: 200,
id: 'description',
disableFilters: true
},
{
Header: 'Mode',
accessor: ({ name }) =>
scanSchema[name] && scanSchema[name].isPassive ? 'Passive' : 'Active',
width: 150,
minWidth: 150,
id: 'mode',
disableFilters: true
},
{
Header: 'Action',
id: 'action',
maxWidth: 100,
Cell: ({ row }: { row: { index: number } }) => {
if (!organization) return;
const enabled = organization.granularScans.find(
(scan) => scan.id === scans[row.index].id
);
return (
<Button
type="button"
onClick={() => {
updateScan(scans[row.index], !enabled);
}}
>
{enabled ? 'Disable' : 'Enable'}
</Button>
);
},
disableFilters: true
}
];
const scanTaskColumns: Column<ScanTask>[] = [
{
Header: 'ID',
accessor: 'id',
disableFilters: true
},
{
Header: 'Status',
accessor: 'status',
disableFilters: true
},
{
Header: 'Type',
accessor: 'type',
disableFilters: true
},
{
Header: 'Name',
accessor: ({ scan }) => scan?.name,
disableFilters: true
},
{
Header: 'Created At',
accessor: ({ createdAt }) => dateAccessor(createdAt),
disableFilters: true,
disableSortBy: true
},
{
Header: 'Requested At',
accessor: ({ requestedAt }) => dateAccessor(requestedAt),
disableFilters: true,
disableSortBy: true
},
{
Header: 'Started At',
accessor: ({ startedAt }) => dateAccessor(startedAt),
disableFilters: true,
disableSortBy: true
},
{
Header: 'Finished At',
accessor: ({ finishedAt }) => dateAccessor(finishedAt),
disableFilters: true,
disableSortBy: true
},
{
Header: 'Output',
accessor: 'output',
disableFilters: true
}
];
const fetchOrganization = useCallback(async () => {
try {
const organization = await apiGet<OrganizationType>(
`/organizations/${organizationId}`
);
organization.scanTasks.sort(
(a, b) =>
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
setOrganization(organization);
setUserRoles(organization.userRoles);
setScanTasks(organization.scanTasks);
const tags = await apiGet<OrganizationTag[]>(`/organizations/tags`);
setTags(tags);
} catch (e) {
console.error(e);
}
}, [apiGet, setOrganization, organizationId]);
const fetchScans = useCallback(async () => {
try {
const response = await apiGet<{
scans: Scan[];
schema: ScanSchema;
}>('/granularScans/');
let { scans } = response;
const { schema } = response;
if (user?.userType !== 'globalAdmin')
scans = scans.filter(
(scan) =>
scan.name !== 'censysIpv4' && scan.name !== 'censysCertificates'
);
setScans(scans);
setScanSchema(schema);
} catch (e) {
console.error(e);
}
}, [apiGet, user]);
const approveUser = async (user: number) => {
try {
await apiPost(
`/organizations/${organization?.id}/roles/${organization?.userRoles[user].id}/approve`,
{ body: {} }
);
const copy = userRoles.map((role, id) =>
id === user ? { ...role, approved: true } : role
);
setUserRoles(copy);
} catch (e) {
console.error(e);
}
};
const removeUser = async (user: number) => {
try {
await apiPost(
`/organizations/${organization?.id}/roles/${userRoles[user].id}/remove`,
{ body: {} }
);
const copy = userRoles.filter((_, ind) => ind !== user);
setUserRoles(copy);
} catch (e) {
console.error(e);
}
};
const updateOrganization = async (body: any) => {
try {
const org = await apiPut('/organizations/' + organization?.id, {
body: organization
});
setOrganization(org);
setFeedbackMessage({
message: 'Organization successfully updated',
type: 'success'
});
} catch (e) {
setFeedbackMessage({
message:
e.status === 422
? 'Error updating organization'
: e.message ?? e.toString(),
type: 'error'
});
console.error(e);
}
};
const updateScan = async (scan: Scan, enabled: boolean) => {
try {
if (!organization) return;
await apiPost(
`/organizations/${organization?.id}/granularScans/${scan.id}/update`,
{
body: {
enabled
}
}
);
setOrganization({
...organization,
granularScans: enabled
? organization.granularScans.concat([scan])
: organization.granularScans.filter(
(granularScan) => granularScan.id !== scan.id
)
});
} catch (e) {
setFeedbackMessage({
message:
e.status === 422 ? 'Error updating scan' : e.message ?? e.toString(),
type: 'error'
});
console.error(e);
}
};
useEffect(() => {
fetchOrganization();
}, [fetchOrganization]);
const onInviteUserSubmit = async () => {
try {
const body = {
firstName: newUserValues.firstName,
lastName: newUserValues.lastName,
email: newUserValues.email,
organization: organization?.id,
organizationAdmin: newUserValues.role === 'admin'
};
const user: User = await apiPost('/users/', {
body
});
const newRole = user.roles[user.roles.length - 1];
newRole.user = user;
if (userRoles.find((role) => role.user.id === user.id)) {
setUserRoles(
userRoles.map((role) => (role.user.id === user.id ? newRole : role))
);
} else {
setUserRoles(userRoles.concat([newRole]));
}
} catch (e) {
setFeedbackMessage({
message:
e.status === 422 ? 'Error inviting user' : e.message ?? e.toString(),
type: 'error'
});
console.log(e);
}
};
const onInviteUserTextChange: React.ChangeEventHandler<
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
> = (e) => onInviteUserChange(e.target.name, e.target.value);
const onInviteUserChange = (name: string, value: any) => {
setNewUserValues((values) => ({
...values,
[name]: value
}));
};
const filter = createFilterOptions<AutocompleteType>();
const ListInput = (props: {
type: 'rootDomains' | 'ipBlocks' | 'tags';
label: string;
}) => {
if (!organization) return null;
const elements: (string | OrganizationTag)[] = organization[props.type];
return (
<div className={classes.headerRow}>
<label>{props.label}</label>
<span>
{elements &&
elements.map((value: string | OrganizationTag, index: number) => (
<Chip
className={classes.chip}
key={index}
label={typeof value === 'string' ? value : value.name}
onDelete={() => {
organization[props.type].splice(index, 1);
setOrganization({ ...organization });
}}
></Chip>
))}
<Chip
label="ADD"
variant="outlined"
color="secondary"
onClick={() => {
setDialog({
open: true,
type: props.type,
label: props.label
});
}}
/>
</span>
</div>
);
};
if (!organization) return null;
const views = [
<Paper className={classes.settingsWrapper} key={0}>
<Dialog
open={dialog.open}
onClose={() => setDialog({ open: false })}
aria-labelledby="form-dialog-title"
maxWidth="xs"
fullWidth
>
<DialogTitle id="form-dialog-title">
Add {dialog.label && dialog.label.slice(0, -1)}
</DialogTitle>
<DialogContent>
{dialog.type === 'tags' ? (
<>
<DialogContentText>
Select an existing tag or add a new one.
</DialogContentText>
<Autocomplete
value={tagValue}
onChange={(event, newValue) => {
if (typeof newValue === 'string') {
setTagValue({
name: newValue
});
} else {
setTagValue(newValue);
}
}}
filterOptions={(options, params) => {
const filtered = filter(options, params);
// Suggest the creation of a new value
if (
params.inputValue !== '' &&
!filtered.find(
(tag) =>
tag.name?.toLowerCase() ===
params.inputValue.toLowerCase()
)
) {
filtered.push({
name: params.inputValue,
title: `Add "${params.inputValue}"`
});
}
return filtered;
}}
selectOnFocus
clearOnBlur
handleHomeEndKeys
options={tags}
getOptionLabel={(option) => {
return option.name ?? '';
}}
renderOption={(option) => {
if (option.title) return option.title;
return option.name ?? '';
}}
fullWidth
freeSolo
renderInput={(params) => (
<TextField {...params} variant="outlined" />
)}
/>
</>
) : (
<TextField
autoFocus
margin="dense"
id="name"
label={dialog.label && dialog.label.slice(0, -1)}
type="text"
fullWidth
onChange={(e) => setInputValue(e.target.value)}
/>
)}
</DialogContent>
<DialogActions>
<Button variant="outlined" onClick={() => setDialog({ open: false })}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
onClick={() => {
if (dialog.type && dialog.type !== 'tags') {
if (inputValue) {
organization[dialog.type].push(inputValue);
setOrganization({ ...organization });
}
} else {
if (tagValue) {
if (!organization.tags) organization.tags = [];
organization.tags.push(tagValue as any);
setOrganization({ ...organization });
}
}
setDialog({ open: false });
setInputValue('');
setTagValue(null);
}}
>
Add
</Button>
</DialogActions>
</Dialog>
<TextField
value={organization.name}
disabled
variant="filled"
InputProps={{
className: classes.orgName
}}
></TextField>
<ListInput label="Root Domains" type="rootDomains"></ListInput>
<ListInput label="IP Blocks" type="ipBlocks"></ListInput>
<ListInput label="Tags" type="tags"></ListInput>
<div className={classes.headerRow}>
<label>Passive Mode</label>
<span>
<SwitchInput
checked={organization.isPassive}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setOrganization({
...organization,
isPassive: event.target.checked
});
}}
color="primary"
/>
</span>
</div>
<div className={classes.buttons}>
<Link to={`/organizations`}>
<Button
variant="outlined"
style={{ marginRight: '10px', color: '#565C65' }}
>
Cancel
</Button>
</Link>
<Button
variant="contained"
onClick={updateOrganization}
style={{ background: '#565C65', color: 'white' }}
>
Save
</Button>
</div>
</Paper>,
<React.Fragment key={1}>
<Table<Role> columns={userRoleColumns} data={userRoles} />
<Dialog
open={dialog.open}
onClose={() => setDialog({ open: false })}
aria-labelledby="form-dialog-title"
maxWidth="xs"
fullWidth
>
<DialogTitle id="form-dialog-title">Add Member</DialogTitle>
<DialogContent>
<p style={{ color: '#3D4551' }}>
Organization members can view Organization-specific vulnerabilities,
domains, and notes. Organization administrators can additionally
manage members and update the organization.
</p>
<TextField
margin="dense"
id="firstName"
name="firstName"
label="First Name"
type="text"
fullWidth
value={newUserValues.firstName}
onChange={onInviteUserTextChange}
variant="filled"
InputProps={{
className: classes.textField
}}
/>
<TextField
margin="dense"
id="lastName"
name="lastName"
label="Last Name"
type="text"
fullWidth
value={newUserValues.lastName}
onChange={onInviteUserTextChange}
variant="filled"
InputProps={{
className: classes.textField
}}
/>
<TextField
margin="dense"
id="email"
name="email"
label="Email"
type="text"
fullWidth
value={newUserValues.email}
onChange={onInviteUserTextChange}
variant="filled"
InputProps={{
className: classes.textField
}}
/>
<br></br>
<br></br>
<FormLabel component="legend">Role</FormLabel>
<RadioGroup
aria-label="role"
name="role"
value={newUserValues.role}
onChange={onInviteUserTextChange}
>
<FormControlLabel
value="standard"
control={<Radio color="primary" />}
label="Standard"
/>
<FormControlLabel
value="admin"
control={<Radio color="primary" />}
label="Administrator"
/>
</RadioGroup>
</DialogContent>
<DialogActions>
<Button variant="outlined" onClick={() => setDialog({ open: false })}>
Cancel
</Button>
<Button
variant="contained"
color="primary"
onClick={async () => {
onInviteUserSubmit();
setDialog({ open: false });
}}
>
Add
</Button>
</DialogActions>
</Dialog>
</React.Fragment>,
<React.Fragment key={2}>
<OrganizationList parent={organization}></OrganizationList>
</React.Fragment>,
<React.Fragment key={3}>
<Table<Scan> columns={scanColumns} data={scans} fetchData={fetchScans} />
<h2>Organization Scan History</h2>
<Table<ScanTask> columns={scanTaskColumns} data={scanTasks} />
</React.Fragment>
];
let navItems = [
{
title: 'Settings',
path: `/organizations/${organizationId}`,
exact: true
},
{
title: 'Members',
path: `/organizations/${organizationId}/members`
}
];
if (!organization.parent) {
navItems = navItems.concat([
// { title: 'Teams', path: `/organizations/${organizationId}/teams` },
{ title: 'Scans', path: `/organizations/${organizationId}/scans` }
]);
}
return (
<div>
<div className={classes.header}>
<h1 className={classes.headerLabel}>
<Link to="/organizations">Organizations</Link>
{organization.parent && (
<>
<ChevronRight></ChevronRight>
<Link to={'/organizations/' + organization.parent.id}>
{organization.parent.name}
</Link>
</>
)}
<ChevronRight
style={{
verticalAlign: 'middle',
lineHeight: '100%',
fontSize: '26px'
}}
></ChevronRight>
<span style={{ color: '#07648D' }}>{organization.name}</span>
</h1>
<Subnav
items={navItems}
styles={{
background: '#F9F9F9'
}}
></Subnav>
</div>
<div className={classes.root}>
<Switch>
<Route
path="/organizations/:organizationId"
exact
render={() => views[0]}
/>
<Route
path="/organizations/:organizationId/members"
render={() => views[1]}
/>
<Route
path="/organizations/:organizationId/teams"
render={() => views[2]}
/>
<Route
path="/organizations/:organizationId/scans"
render={() => views[3]}
/>
</Switch>
</div>
</div>
);
}
Example #3
Source File: Scan.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
ScanComponent: React.FC = () => {
const { scanId } = useParams();
const { apiGet, apiPut, setFeedbackMessage } = useAuthContext();
const [scan, setScan] = useState<Scan>();
const [organizationOptions, setOrganizationOptions] = useState<
OrganizationOption[]
>([]);
const [tags, setTags] = useState<OrganizationTag[]>([]);
const [scanSchema, setScanSchema] = useState<ScanSchema>({});
const [values, setValues] = useState<ScanFormValues>({
name: 'scan.name',
arguments: '{}',
organizations: [],
frequency: 1,
frequencyUnit: 'minute',
isGranular: false,
isUserModifiable: false,
isSingleScan: false,
tags: []
});
const fetchScan = useCallback(async () => {
try {
const { scan, schema, organizations } = await apiGet<{
scan: Scan;
schema: ScanSchema;
organizations: OrganizationType[];
}>(`/scans/${scanId}`);
setScan(scan);
setDefaultValues(scan);
setScanSchema(schema);
setOrganizationOptions(
organizations.map((e) => ({ label: e.name, value: e.id }))
);
const tags = await apiGet<OrganizationTag[]>(`/organizations/tags`);
setTags(tags);
} catch (e) {
console.error(e);
}
}, [apiGet, setScan, scanId]);
const onSubmit = async (body: ScanFormValues) => {
try {
setFrequency(body);
await apiPut(`/scans/${scanId}`, {
body: {
...body,
organizations: body.organizations
? body.organizations.map((e) => e.value)
: [],
tags: body.tags
? body.tags.map((e) => ({
id: e.value
}))
: []
}
});
setFeedbackMessage({
message: 'Scan successfully updated',
type: 'success'
});
} catch (e) {
setFeedbackMessage({
message: 'Error updating scan',
type: 'error'
});
console.log(e);
}
};
const setDefaultValues = async (scan: Scan) => {
let oldFrequencyUnit = 'minute';
if (scan.frequency >= 86400) {
scan.frequency = scan.frequency / (60 * 60 * 24);
oldFrequencyUnit = 'day';
} else if (scan.frequency >= 3600) {
scan.frequency = scan.frequency / (60 * 60);
oldFrequencyUnit = 'hour';
} else if (scan.frequency >= 60) {
scan.frequency = scan.frequency / 60;
oldFrequencyUnit = 'minute';
}
setValues((values) => ({
...values,
name: scan.name,
frequency: scan.frequency,
frequencyUnit: oldFrequencyUnit,
isGranular: scan.isGranular,
isSingleScan: scan.isSingleScan
}));
//retrieves the organizations that are currently
//associated with this scan
if (scan.isGranular) {
const defaultOrganizations: OrganizationOption[] = [];
for (const org in scan.organizations) {
const thisOrganization: OrganizationType = scan.organizations[org];
const thisOrganizationOption: OrganizationOption = {
label: thisOrganization.name,
value: thisOrganization.id
};
defaultOrganizations.push(thisOrganizationOption);
}
setValues((values) => ({
...values,
organizations: defaultOrganizations,
tags: scan.tags.map((tag) => ({
label: tag.name,
value: tag.id
}))
}));
}
};
const isGlobal = scanSchema.global;
useEffect(() => {
fetchScan();
}, [fetchScan]);
if (!scan)
return (
<div className={classes.root}>
<h1>No results found</h1>
</div>
);
return (
<div className={classes.root}>
<Header>
<div className="usa-nav-container">
<div className="usa-navbar">
<h1>{'Edit ' + scan.name}</h1>
</div>
</div>
</Header>
<ScanForm
organizationOption={organizationOptions}
tags={tags}
propValues={values}
global={isGlobal}
onSubmit={onSubmit}
type="edit"
scan={scan}
scanSchema={scanSchema}
></ScanForm>
</div>
);
}
Example #4
Source File: ScansView.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
ScansView: React.FC = () => {
const { apiGet, apiPost, apiDelete } = useAuthContext();
const [showModal, setShowModal] = useState<Boolean>(false);
const [selectedRow, setSelectedRow] = useState<number>(0);
const [scans, setScans] = useState<Scan[]>([]);
const [organizationOptions, setOrganizationOptions] = useState<
OrganizationOption[]
>([]);
const [tags, setTags] = useState<OrganizationTag[]>([]);
const [scanSchema, setScanSchema] = useState<ScanSchema>({});
const columns: Column<Scan>[] = [
{
Header: 'Run',
id: 'run',
Cell: ({ row }: { row: { index: number } }) => (
<div
style={{ textAlign: 'center' }}
onClick={() => {
runScan(row.index);
}}
>
<FaPlayCircle />
</div>
),
disableFilters: true
},
{
Header: 'Name',
accessor: 'name',
width: 200,
id: 'name',
disableFilters: true
},
{
Header: 'Tags',
accessor: ({ tags }) => tags.map((tag) => tag.name).join(', '),
width: 150,
minWidth: 150,
id: 'tags',
disableFilters: true
},
{
Header: 'Mode',
accessor: ({ name }) =>
scanSchema[name] && scanSchema[name].isPassive ? 'Passive' : 'Active',
width: 150,
minWidth: 150,
id: 'mode',
disableFilters: true
},
{
Header: 'Frequency',
accessor: ({ frequency, isSingleScan }) => {
let val, unit;
if (frequency < 60 * 60) {
val = frequency / 60;
unit = 'minute';
} else if (frequency < 60 * 60 * 24) {
val = frequency / (60 * 60);
unit = 'hour';
} else {
val = frequency / (60 * 60 * 24);
unit = 'day';
}
if (isSingleScan) {
return 'Single Scan';
}
return `Every ${val} ${unit}${val === 1 ? '' : 's'}`;
},
width: 200,
id: 'frequency',
disableFilters: true
},
{
Header: 'Last Run',
accessor: (args: Scan) => {
return !args.lastRun ||
new Date(args.lastRun).getTime() === new Date(0).getTime()
? 'None'
: `${formatDistanceToNow(parseISO(args.lastRun))} ago`;
},
width: 200,
id: 'lastRun',
disableFilters: true
},
{
Header: 'Edit',
id: 'edit',
Cell: ({ row }: CellProps<Scan>) => (
<Link to={`/scans/${row.original.id}`} style={{ color: 'black' }}>
<FaEdit />
</Link>
),
disableFilters: true
},
{
Header: 'Delete',
id: 'delete',
Cell: ({ row }: { row: { index: number } }) => (
<span
onClick={() => {
setShowModal(true);
setSelectedRow(row.index);
}}
>
<FaTimes />
</span>
),
disableFilters: true
},
{
Header: 'Description',
accessor: ({ name }) => scanSchema[name]?.description,
width: 200,
maxWidth: 200,
id: 'description',
disableFilters: true
}
];
const [errors, setErrors] = useState<Errors>({});
const [values] = useState<ScanFormValues>({
name: 'censys',
arguments: '{}',
organizations: [],
frequency: 1,
frequencyUnit: 'minute',
isGranular: false,
isUserModifiable: false,
isSingleScan: false,
tags: []
});
React.useEffect(() => {
document.addEventListener('keyup', (e) => {
//Escape
if (e.keyCode === 27) {
setShowModal(false);
}
});
}, [apiGet]);
const fetchScans = useCallback(async () => {
try {
const { scans, organizations, schema } = await apiGet<{
scans: Scan[];
organizations: Organization[];
schema: ScanSchema;
}>('/scans/');
const tags = await apiGet<OrganizationTag[]>(`/organizations/tags`);
setScans(scans);
setScanSchema(schema);
setOrganizationOptions(
organizations.map((e) => ({ label: e.name, value: e.id }))
);
setTags(tags);
} catch (e) {
console.error(e);
}
}, [apiGet]);
const deleteRow = async (index: number) => {
try {
const row = scans[index];
await apiDelete(`/scans/${row.id}`, { body: {} });
setScans(scans.filter((scan) => scan.id !== row.id));
} catch (e) {
setErrors({
global:
e.status === 422 ? 'Unable to delete scan' : e.message ?? e.toString()
});
console.log(e);
}
};
const onSubmit = async (body: ScanFormValues) => {
try {
// For now, parse the arguments as JSON. We'll want to add a GUI for this in the future
body.arguments = JSON.parse(body.arguments);
setFrequency(body);
const scan = await apiPost('/scans/', {
body: {
...body,
organizations: body.organizations
? body.organizations.map((e) => e.value)
: [],
tags: body.tags ? body.tags.map((e) => ({ id: e.value })) : []
}
});
setScans(scans.concat(scan));
} catch (e) {
setErrors({
global: e.message ?? e.toString()
});
console.log(e);
}
};
const invokeScheduler = async () => {
setErrors({ ...errors, scheduler: '' });
try {
await apiPost('/scheduler/invoke', { body: {} });
} catch (e) {
console.error(e);
setErrors({ ...errors, scheduler: 'Invocation failed.' });
}
};
/**
* Manually runs a single scan, then immediately invokes the
* scheduler so the scan is run.
* @param index Row index
*/
const runScan = async (index: number) => {
const row = scans[index];
try {
await apiPost(`/scans/${row.id}/run`, { body: {} });
} catch (e) {
console.error(e);
setErrors({ ...errors, scheduler: 'Run failed.' });
}
await invokeScheduler();
};
return (
<>
<Table<Scan> columns={columns} data={scans} fetchData={fetchScans} />
<br></br>
<Button type="submit" outline onClick={invokeScheduler}>
Manually run scheduler
</Button>
{errors.scheduler && <p className={classes.error}>{errors.scheduler}</p>}
<h2>Add a scan</h2>
{errors.global && <p className={classes.error}>{errors.global}</p>}
<ScanForm
organizationOption={organizationOptions}
tags={tags}
propValues={values}
onSubmit={onSubmit}
type="create"
scanSchema={scanSchema}
></ScanForm>
<ImportExport<Scan>
name="scans"
fieldsToExport={['name', 'arguments', 'frequency']}
onImport={async (results) => {
// TODO: use a batch call here instead.
const createdScans = [];
for (const result of results) {
createdScans.push(
await apiPost('/scans/', {
body: {
...result,
// These fields are initially parsed as strings, so they need
// to be converted to objects.
arguments: JSON.parse(
((result.arguments as unknown) as string) || ''
)
}
})
);
}
setScans(scans.concat(...createdScans));
}}
getDataToExport={() =>
scans.map((scan) => ({
...scan,
arguments: JSON.stringify(scan.arguments)
}))
}
/>
{showModal && (
<div>
<Overlay />
<ModalContainer>
<Modal
actions={
<>
<Button
outline
type="button"
onClick={() => {
setShowModal(false);
}}
>
Cancel
</Button>
<Button
type="button"
onClick={() => {
deleteRow(selectedRow);
setShowModal(false);
}}
>
Delete
</Button>
</>
}
title={<h2>Delete scan?</h2>}
>
<p>
Are you sure you would like to delete the{' '}
<code>{scans[selectedRow].name}</code> scan?
</p>
</Modal>
</ModalContainer>
</div>
)}
</>
);
}